摘要: 隨着2024年“低空經濟”首次寫入政府工作報告,以及“具身智能”成為AI領域的下一個高地,實時視頻傳輸技術正面臨前所未有的挑戰。無人機的高速巡檢、機器人的遠程遙操作(Teleoperation),都對視頻流的延遲提出了毫秒級的苛刻要求。本文將結合行業趨勢,深度剖析基於大牛直播SDK(SmartMediakit)的超低延遲RTMP/RTSP播放器技術,探討如何在弱網環境下實現“所見即所得”的極致視覺反饋。
一、 引言:當“看見”成為控制的瓶頸
在傳統的視頻直播場景(如電商帶貨、賽事直播)中,3秒甚至5秒的延遲是可以被接受的。然而,在低空經濟(無人機物流、應急救援)和具身智能(人形機器人、遠程醫療)場景下,視頻不僅僅是內容,更是傳感器數據。
編輯
- 無人機場景:當無人機以50km/h的速度飛行時,1秒的圖像延遲意味着飛手看到的畫面是14米之前的位置。這對於超視距飛行(BVLOS)是致命的安全隱患。
- 具身智能場景:在遠程操控機器人排爆或手術時,如果手部動作與視覺反饋不同步,操作者會產生嚴重的眩暈感(VR暈動症),且無法完成精細操作。
行業共識是:端到端延遲(Glass-to-Glass Latency)必須控制在200ms以內,甚至更低,才能滿足實時操控的需求。
二、 通用協議的困境與大牛直播SDK的破局
目前主流的流媒體協議中,HLS/DASH延遲過高(10s+),WebRTC雖然延遲低但部署複雜且很難兼容傳統的安防/廣電架構。RTMP和RTSP依然是目前工業界、安防界最成熟、兼容性最好的選擇。
然而,開源播放器(如FFmpeg命令行、VLC、IJKPlayer默認配置)在處理這兩個協議時,通常會有幾秒的緩存。
大牛直播SDK(SmartPlayer)的核心價值在於:在保持RTMP/RTSP標準協議兼容性的前提下,通過自研的各種策略,將延遲壓榨到了極致(100-200ms)。
核心技術點解析
- 極速Jitter Buffer策略 傳統播放器為了抗抖動,會預設較大的Buffer。大牛直播SDK採用了動態Buffer策略,在網絡良好的情況下,可以配置為0緩衝模式,數據收到即解碼,解碼即渲染,實現真正的實時播放。
- 多路H.265硬件解碼 低空經濟和機器人往往依賴4K甚至8K的高清圖像來識別細節。SDK支持Android/iOS/Windows全平台的H.265硬解。不僅降低了CPU佔用和設備發熱(對電池供電的無人機至關重要),還能在同等畫質下比H.264節省的帶寬。
- 追幀與丟幀算法 在弱網環境下,累積延遲是最大的敵人。SDK內置了智能的追幀邏輯:當檢測到網絡擁塞導致緩存堆積時,播放器會策略性地丟棄非關鍵幀(P幀),加速解碼播放,確保當前看到的畫面是最新的,而不是幾秒前的“歷史”。
- RTSP的TCP/UDP自動切換 針對無人機圖傳經常面臨的鏈路干擾,SDK支持RTSP over TCP(抗丟包,更穩定)和UDP(低延遲,更激進)模式,並支持組播技術,適應複雜的內網分發場景。
SmartMediakit 的設計理念總結
|
傳統播放器
|
SmartPlayer
|
|
“內容播放”
|
“實時決策反饋”
|
|
穩定優先
|
延遲優先(在穩定範圍內)
|
|
防卡頓靠加長緩存
|
防卡頓靠智能追幀與傳輸調優
|
|
默認直播配置
|
面向工業生產的實時渲染鏈路
|
一句話總結:
RTSP/RTMP 不是問題
播放器才是整個實時鏈路的命門
SmartPlayer,讓它變成系統最堅實的一環
三、 場景實戰:低空經濟與具身智能的落地
1. 無人機城市空中交通(UAM)與巡檢
在城市樓宇間穿梭的物流無人機,面臨複雜的電磁干擾和遮擋。
- 推流端:利用大牛直播SDK的輕量級RTSP/RTMP推送端,直接採集無人機機載攝像頭(HDMI/SDI轉IP)數據,進行H.265編碼。
- 播放端:地面站控制枱集成SmartPlayer。
- 關鍵配置:開啓
LowLatencyMode,設置緩衝時間為0。 - 效果:飛手推杆加速,畫面幾乎同步發生變化,延遲感微乎其微。
2. 具身智能機器人的“千里眼”
假設我們在北京,需要操控一台位於深圳的數據中心巡檢機器人。
- 架構:機器人內部運行Linux ARM工控機,運行RTSP服務模塊。
- 傳輸:通過5G切片網絡或SD-WAN回傳。
- 雙向語音:大牛直播SDK不僅支持視頻低延遲,還支持雙向實時語音通話。操作員不僅能看到現場,還能聽到現場設備的異響,並實時喊話指揮。這一特性對於具身智能的“交互性”至關重要。
3.為什麼這類行業對播放器能力有“剛性依賴”
|
指標影響
|
UAM無人機
|
具身智能機器人
|
|
延遲偏高(300ms+)
|
誤差增大 → 失控風險
|
指令滯後 → 機械臂危險動作
|
|
弱網抗性不足
|
花屏/卡頓 → 飛行風險
|
畫面不同步 → 錯誤判斷
|
|
無雙向音視頻能力
|
無法本地協作
|
無法反饋環境聲學信息
|
SmartMediakit在這些核心指標上均提供工程化保障
確保遙操作不是“希望能行”,而是“必須能行”
四、 核心代碼邏輯示例(Android端為例)
要實現超低延遲,單純引入SDK是不夠的,關鍵在於參數的配置。以下是基於大牛直播SDK實現低延遲播放的關鍵代碼邏輯(Java):
onCreate()時,先new SmartPlayerJniV2():
/*
* SmartPlayer.java
* Author: daniusdk.com
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_smart_player);
...
libPlayer = new SmartPlayerJniV2();
myContext = this.getApplicationContext();
}
開始播放、停止播放實現,開始播放的時候,調用InitAndSetConfig(),完成常規參數初始化,然後調用僅播放相關的其他接口。
btnStartStopPlayback.setOnClickListener(new Button.OnClickListener() {
// @Override
public void onClick(View v) {
if (isPlaying) {
Log.i(TAG, "Stop playback stream++");
int iRet = libPlayer.SmartPlayerStopPlay(playerHandle);
if (iRet != 0) {
Log.e(TAG, "Call SmartPlayerStopPlay failed..");
return;
}
btnHardwareDecoder.setEnabled(true);
btnLowLatency.setEnabled(true);
if (!isRecording) {
btnPopInputUrl.setEnabled(true);
btnSetPlayBuffer.setEnabled(true);
btnFastStartup.setEnabled(true);
btnRecoderMgr.setEnabled(true);
libPlayer.SmartPlayerClose(playerHandle);
playerHandle = 0;
}
isPlaying = false;
btnStartStopPlayback.setText("開始播放 ");
if (is_enable_hardware_render_mode && sSurfaceView != null) {
sSurfaceView.setVisibility(View.GONE);
sSurfaceView.setVisibility(View.VISIBLE);
}
Log.i(TAG, "Stop playback stream--");
} else {
Log.i(TAG, "Start playback stream++");
if (!isRecording) {
InitAndSetConfig();
}
// 如果第二個參數設置為null,則播放純音頻
libPlayer.SmartPlayerSetSurface(playerHandle, sSurfaceView);
libPlayer.SmartPlayerSetRenderScaleMode(playerHandle, 1);
//int render_format = 1;
//libPlayer.SmartPlayerSetSurfaceRenderFormat(playerHandle, render_format);
//int is_enable_anti_alias = 1;
//libPlayer.SmartPlayerSetSurfaceAntiAlias(playerHandle, is_enable_anti_alias);
if (isHardwareDecoder && is_enable_hardware_render_mode) {
libPlayer.SmartPlayerSetHWRenderMode(playerHandle, 1);
}
// External Render test
//libPlayer.SmartPlayerSetExternalRender(playerHandle, new RGBAExternalRender(imageSavePath));
//libPlayer.SmartPlayerSetExternalRender(playerHandle, new I420ExternalRender(imageSavePath));
libPlayer.SmartPlayerSetUserDataCallback(playerHandle, new UserDataCallback());
//libPlayer.SmartPlayerSetSEIDataCallback(playerHandle, new SEIDataCallback());
libPlayer.SmartPlayerSetAudioOutputType(playerHandle, 1);
if (isMute) {
libPlayer.SmartPlayerSetMute(playerHandle, isMute ? 1
: 0);
}
if (isHardwareDecoder) {
int isSupportHevcHwDecoder = libPlayer.SetSmartPlayerVideoHevcHWDecoder(playerHandle, 1);
int isSupportH264HwDecoder = libPlayer
.SetSmartPlayerVideoHWDecoder(playerHandle, 1);
Log.i(TAG, "isSupportH264HwDecoder: " + isSupportH264HwDecoder + ", isSupportHevcHwDecoder: " + isSupportHevcHwDecoder);
}
libPlayer.SmartPlayerSetLowLatencyMode(playerHandle, isLowLatency ? 1
: 0);
libPlayer.SmartPlayerSetFlipVertical(playerHandle, is_flip_vertical ? 1 : 0);
libPlayer.SmartPlayerSetFlipHorizontal(playerHandle, is_flip_horizontal ? 1 : 0);
libPlayer.SmartPlayerSetRotation(playerHandle, rotate_degrees);
libPlayer.SmartPlayerSetAudioVolume(playerHandle, curAudioVolume);
int iPlaybackRet = libPlayer
.SmartPlayerStartPlay(playerHandle);
if (iPlaybackRet != 0) {
Log.e(TAG, "Call SmartPlayerStartPlay failed..");
return;
}
btnStartStopPlayback.setText("停止播放 ");
btnPopInputUrl.setEnabled(false);
btnPopInputKey.setEnabled(false);
btnSetPlayBuffer.setEnabled(false);
btnLowLatency.setEnabled(false);
btnFastStartup.setEnabled(false);
btnRecoderMgr.setEnabled(false);
isPlaying = true;
Log.i(TAG, "Start playback stream--");
}
}
});
由於RTSP、RTMP播放模塊,除了常規的直播播放外,也可能錄像、或者實時拉流轉發到RTMP服務器或輕量級RTSP服務,所以,和錄像、轉發相關的播放端基礎參數配置,放到InitAndSetConfig()實現:
private void InitAndSetConfig() {
playerHandle = libPlayer.SmartPlayerOpen(myContext);
if (playerHandle == 0) {
Log.e(TAG, "surfaceHandle with nil..");
return;
}
libPlayer.SetSmartPlayerEventCallbackV2(playerHandle,
new EventHandeV2());
libPlayer.SmartPlayerSetBuffer(playerHandle, playBuffer);
// set report download speed(默認2秒一次回調 用户可自行調整report間隔)
libPlayer.SmartPlayerSetReportDownloadSpeed(playerHandle, 1, 2);
libPlayer.SmartPlayerSetFastStartup(playerHandle, isFastStartup ? 1 : 0);
//設置RTSP超時時間
int rtsp_timeout = 10;
libPlayer.SmartPlayerSetRTSPTimeout(playerHandle, rtsp_timeout);
//設置RTSP TCP/UDP模式自動切換
int is_auto_switch_tcp_udp = 1;
libPlayer.SmartPlayerSetRTSPAutoSwitchTcpUdp(playerHandle, is_auto_switch_tcp_udp);
libPlayer.SmartPlayerSaveImageFlag(playerHandle, 1);
// It only used when playback RTSP stream..
// libPlayer.SmartPlayerSetRTSPTcpMode(playerHandle, 1);
// playbackUrl = "rtmp://localhost:1935/live/stream1";
if (playbackUrl == null) {
Log.e(TAG, "playback URL with NULL...");
return;
}
libPlayer.SmartPlayerSetUrl(playerHandle, playbackUrl);
// try_set_rtsp_url(playbackUrl);
}
EventHandle播放端事件回調處理,是底層狀態反饋非常重要的媒介,除了網絡狀態、buffering狀態回調外、還有錄像狀態、快照狀態等回調:
class EventHandeV2 implements NTSmartEventCallbackV2 {
@Override
public void onNTSmartEventCallbackV2(long handle, int id, long param1,
long param2, String param3, String param4, Object param5) {
String player_event = "";
switch (id) {
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_STARTED:
player_event = "開始..";
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTING:
player_event = "連接中..";
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTION_FAILED:
player_event = "連接失敗..";
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTED:
player_event = "連接成功..";
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_DISCONNECTED:
player_event = "連接斷開..";
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_STOP:
player_event = "停止播放..";
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_RESOLUTION_INFO:
player_event = "分辨率信息: width: " + param1 + ", height: " + param2;
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_NO_MEDIADATA_RECEIVED:
player_event = "收不到媒體數據,可能是url錯誤..";
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_SWITCH_URL:
player_event = "切換播放URL..";
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_CAPTURE_IMAGE:
player_event = "快照: " + param1 + " 路徑:" + param3;
if (param1 == 0)
player_event = player_event + ", 截取快照成功";
else
player_event = player_event + ", 截取快照失敗";
if (param4 != null && !param4.isEmpty())
player_event += (", user data:" + param4);
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_RECORDER_START_NEW_FILE:
player_event = "[record]開始一個新的錄像文件 : " + param3;
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_ONE_RECORDER_FILE_FINISHED:
player_event = "[record]已生成一個錄像文件 : " + param3;
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_START_BUFFERING:
Log.i(TAG, "Start Buffering");
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_BUFFERING:
Log.i(TAG, "Buffering:" + param1 + "%");
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_STOP_BUFFERING:
Log.i(TAG, "Stop Buffering");
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_DOWNLOAD_SPEED:
player_event = "download_speed:" + param1 + "Byte/s" + ", "
+ (param1 * 8 / 1000) + "kbps" + ", " + (param1 / 1024)
+ "KB/s";
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_RTSP_STATUS_CODE:
Log.e(TAG, "RTSP error code received, please make sure username/password is correct, error code:" + param1);
player_event = "RTSP error code:" + param1;
break;
}
if (player_event.length() > 0) {
Log.i(TAG, player_event);
Message message = new Message();
message.what = PLAYER_EVENT_MSG;
message.obj = player_event;
handler.sendMessage(message);
}
}
}
如果RTSP、RTMP流需要錄像:
btnStartStopRecorder.setOnClickListener(new Button.OnClickListener() {
// @Override
public void onClick(View v) {
if (isRecording) {
int iRet = libPlayer.SmartPlayerStopRecorder(playerHandle);
if (iRet != 0) {
Log.e(TAG, "Call SmartPlayerStopRecorder failed..");
return;
}
if (!isPlaying) {
btnPopInputUrl.setEnabled(true);
btnSetPlayBuffer.setEnabled(true);
btnFastStartup.setEnabled(true);
btnRecoderMgr.setEnabled(true);
libPlayer.SmartPlayerClose(playerHandle);
playerHandle = 0;
}
btnStartStopRecorder.setText(" 開始錄像");
isRecording = false;
} else {
Log.i(TAG, "onClick start recorder..");
if (!isPlaying) {
InitAndSetConfig();
}
ConfigRecorderFunction();
int startRet = libPlayer.SmartPlayerStartRecorder(playerHandle);
if (startRet != 0) {
Log.e(TAG, "Failed to start recorder.");
return;
}
btnPopInputUrl.setEnabled(false);
btnSetPlayBuffer.setEnabled(false);
btnFastStartup.setEnabled(false);
btnRecoderMgr.setEnabled(false);
isRecording = true;
btnStartStopRecorder.setText("停止錄像");
}
}
});
其中,錄像參數配置選項設置如下,除了下面演示接口外,還可以設置僅錄視頻或音頻:
void ConfigRecorderFunction() {
if (libPlayer != null) {
int is_rec_trans_code = 1;
libPlayer.SmartPlayerSetRecorderAudioTranscodeAAC(playerHandle, is_rec_trans_code);
if (recDir != null && !recDir.isEmpty()) {
int ret = libPlayer.SmartPlayerCreateFileDirectory(recDir);
if (0 == ret) {
if (0 != libPlayer.SmartPlayerSetRecorderDirectory(
playerHandle, recDir)) {
Log.e(TAG, "Set recoder dir failed , path:" + recDir);
return;
}
if (0 != libPlayer.SmartPlayerSetRecorderFileMaxSize(
playerHandle, 200)) {
Log.e(TAG,
"SmartPublisherSetRecorderFileMaxSize failed.");
return;
}
} else {
Log.e(TAG, "Create recorder dir failed, path:" + recDir);
}
}
}
}
如需播放過程中實時截圖:
btnCaptureImage.setOnClickListener(new Button.OnClickListener() {
@SuppressLint("SimpleDateFormat")
public void onClick(View v) {
if (0 == playerHandle)
return;
if (null == capture_image_date_format_)
capture_image_date_format_ = new SimpleDateFormat("yyyyMMdd_HHmmss_SSS");
String timestamp = capture_image_date_format_.format(new Date());
String imageFileName = timestamp;
String image_path = imageSavePath + "/" + imageFileName;
int quality;
boolean is_jpeg = true;
if (is_jpeg) {
image_path += ".jpeg";
quality = 100;
}
else {
image_path += ".png";
quality = 100;
}
int capture_ret = libPlayer.CaptureImage(playerHandle,is_jpeg?0:1, quality, image_path, "test cix");
Log.i(TAG, "capture image ret:" + capture_ret + ", file:" + image_path);
}
});
如需對視頻view做水平、垂直翻轉或旋轉:
btnFlipVertical.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
is_flip_vertical = !is_flip_vertical;
if (is_flip_vertical) {
btnFlipVertical.setText("取消反轉");
} else {
btnFlipVertical.setText("垂直反轉");
}
if (playerHandle != 0) {
libPlayer.SmartPlayerSetFlipVertical(playerHandle,
is_flip_vertical ? 1 : 0);
}
}
});
btnFlipHorizontal.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
is_flip_horizontal = !is_flip_horizontal;
if (is_flip_horizontal) {
btnFlipHorizontal.setText("取消反轉");
} else {
btnFlipHorizontal.setText("水平反轉");
}
if (playerHandle != 0) {
libPlayer.SmartPlayerSetFlipHorizontal(playerHandle,
is_flip_horizontal ? 1 : 0);
}
}
});
btnRotation.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
rotate_degrees += 90;
rotate_degrees = rotate_degrees % 360;
if (0 == rotate_degrees) {
btnRotation.setText("旋轉90度");
} else if (90 == rotate_degrees) {
btnRotation.setText("旋轉180度");
} else if (180 == rotate_degrees) {
btnRotation.setText("旋轉270度");
} else if (270 == rotate_degrees) {
btnRotation.setText("不旋轉");
}
if (playerHandle != 0) {
libPlayer.SmartPlayerSetRotation(playerHandle,
rotate_degrees);
}
}
});
onDestroy() 的時候,停掉播放、錄像、釋放播放端實例句柄:
@Override
protected void onDestroy() {
Log.i(TAG, "Run into activity destory++");
if (playerHandle != 0) {
if (isPlaying) {
libPlayer.SmartPlayerStopPlay(playerHandle);
}
if (isRecording) {
libPlayer.SmartPlayerStopRecorder(playerHandle);
}
libPlayer.SmartPlayerClose(playerHandle);
playerHandle = 0;
}
super.onDestroy();
finish();
System.exit(0);
}
接口設計
|
Android RTSP|RTMP播放端SDK接口詳解 |
|||
|
調用描述 |
接口 |
接口描述 |
|
|
最先調用,如成功返回播放實例 |
SmartPlayerOpen |
player初始化,設置上下文信息,返回player句柄 |
|
|
Event回調 |
SetSmartPlayerEventCallbackV2 |
設置event callback |
|
|
硬解碼設置(H.264) |
SetSmartPlayerVideoHWDecoder |
設置是否用H.264硬解碼播放,如硬解碼不支持,自動適配到軟解碼 |
|
|
硬解碼設置(H.265) |
SetSmartPlayerVideoHevcHWDecoder |
設置是否用H.265硬解碼播放,如硬解碼不支持,自動適配到軟解碼 |
|
|
視頻畫面 填充模式 |
SmartPlayerSetRenderScaleMode |
設置視頻畫面的填充模式,如填充整個view、等比例填充view,如不設置,默認填充整個view |
|
|
設置SurfaceView模式下render類型 |
SmartPlayerSetSurfaceRenderFormat |
設置SurfaceView模式下(NTRenderer.CreateRenderer第二個參數傳false的情況),render類型 0: RGB565格式,如不設置,默認此模式; 1: ARGB8888格式 |
|
|
設置SurfaceView模式下抗鋸齒效果 |
SmartPlayerSetSurfaceAntiAlias |
設置SurfaceView模式下(NTRenderer.CreateRenderer第二個參數傳false的情況),抗鋸齒效果,注意:抗鋸齒模式開啓後,可能會影像性能,請慎用 |
|
|
設置播放的surface |
SmartPlayerSetSurface |
設置播放的surface,如果為null,則播放純音頻 |
|
|
設置視頻硬解碼下Mediacodec自行繪製模式 |
SmartPlayerSetHWRenderMode |
此種模式下,硬解碼兼容性和效率更好,回調YUV/RGB、快照和圖像等比例縮放功能將不可用 |
|
|
更新硬解碼surface |
SmartPlayerUpdateHWRenderSurface |
設置更新硬解碼surface |
|
|
音頻回調 |
YUV/RGB |
SmartPlayerSetExternalRender |
提供解碼後YUV/RGB數據接口,供用户自己render或進一步處理(如視頻分析) |
|
Audio |
SmartPlayerSetExternalAudioOutput |
回調audio數據到上層(供二次處理之用) |
|
|
audio輸出類型 |
SmartPlayerSetAudioOutputType |
如果use_audiotrack設置為0,將會自動選擇輸出設備,如果設置為1,使用audiotrack模式,一對一回音消除模式下,請選用audiotrack模式 |
|
|
Video輸出類型 |
NTRenderer.CreateRenderer(上層demo內) |
第二個參數,如果是true,用openGLES繪製,false則用默認surfaceView |
|
|
播放模式 |
緩衝時間設置 |
SmartPlayerSetBuffer |
設置播放端緩存數據buffer,單位:毫秒,如不需buffer,設置為0 |
|
首屏秒開 |
SmartPlayerSetFastStartup |
設置快速啓動後,如果CDN緩存GOP,實現首屏秒開 |
|
|
低延遲模式 |
SmartPlayerSetLowLatencyMode |
針對類似於直播娃娃機等期待超低延遲的使用場景,超低延遲播放模式下,延遲可達到200~400ms |
|
|
快速切換URL |
SmartPlayerSwitchPlaybackUrl |
快速切換播放url,快速切換時,只換播放source部分,適用於不同數據流之間,快速切換(如娃娃機雙攝像頭切換或高低分辨率流切換) |
|
|
RTSP TCP/UDP模式設置 |
SmartPlayerSetRTSPTcpMode |
設置RTSP TCP/UDP模式,如不設置,默認UDP模式 |
|
|
RTSP超時時間設置 |
SmartPlayerSetRTSPTimeout |
設置RTSP超時時間,timeout單位為秒,必須大於0 |
|
|
設置RTSP TCP/UDP自動切換 |
SmartPlayerSetRTSPAutoSwitchTcpUdp |
對於RTSP來説,有些可能支持rtp over udp方式,有些可能支持使用rtp over tcp方式 為了方便使用,有些場景下可以開啓自動嘗試切換開關, 打開後如果udp無法播放,sdk會自動嘗試tcp, 如果tcp方式播放不了,sdk會自動嘗試udp. |
|
|
設置RTSP用户名和密碼 |
SetRTSPAuthenticationInfo |
如果RTSP URL已包含用户名和密碼, 此接口設置的用户名和密碼將無效. 就是説要用這個接口設置的用户名和密碼去做認證, RTSP URL不能包含用户名和密碼. |
|
|
實時靜音 |
SmartPlayerSetMute |
實時靜音 |
|
|
設置播放音量 |
SmartPlayerSetAudioVolume |
播放端音量實時調節,範圍[0,100],0時為靜音,100為原始流數據最大音量 |
|
|
設置是否禁用 Enhanced RTMP |
DisableEnhancedRTMP |
disable enhanced RTMP, SDK默認是開啓enhanced RTMP的 |
|
|
實時截圖 |
CaptureImage |
支持JPEG和PNG兩種格式 |
|
|
視頻鏡像旋轉 |
旋轉 |
SmartPlayerSetRotation |
設置順時針旋轉, 注意除了0度之外, 其他角度都會額外消耗性能,當前支持 0度,90度, 180度, 270度 旋轉 |
|
水平反轉 |
SmartPlayerSetFlipHorizontal |
設置視頻水平反轉 |
|
|
垂直反轉 |
SmartPlayerSetFlipVertical |
設置視頻垂直反轉 |
|
|
設置URL |
SmartPlayerSetUrl |
設置需要播放或錄像的RTMP/RTSP url |
|
|
開始播放 |
SmartPlayerStartPlay |
開始播放RTSP/RTMP流 |
|
|
停止播放 |
SmartPlayerStopPlay |
停止播放RTSP/RTMP流 |
|
|
關閉播放實例 |
SmartPlayerClose |
結束時必須調用close接口釋放資源 |
|
功能支持
- 音頻:AAC/Speex(RTMP)/PCMA/PCMU;
- 視頻:H.264、H.265;
- 播放協議:RTSP|RTMP;
- 支持純音頻、純視頻、音視頻播放;
- 支持多實例播放;
- 支持軟解碼,特定機型硬解碼;
- 支持RTSP TCP、UDP模式設置;
- 支持RTSP TCP、UDP模式自動切換;
- 支持RTSP超時時間設置,單位:秒;
- 支持buffer時間設置,單位:毫秒;
- 支持超低延遲模式;
- 支持斷網自動重連、視頻追趕,支持buffer狀態等回調;
- 支持視頻view實時旋轉(0° 90° 180° 270°);
- 支持視頻view水平反轉、垂直反轉;
- 支持Surfaceview/OpenGL ES/TextureView繪製;
- 支持視頻畫面填充模式設置;
- 音頻支持AudioTrack、OpenSL ES模式;
- 支持jpeg、png實時截圖;
- 支持實時音量調節;
- 支持解碼前音視頻數據回調;
- 支持解碼後YUV/RGB數據回調;
- 支持Enhanced RTMP;
- 支持擴展錄像功能;
- 支持Android 5.1及以上版本。
五、總結與展望
低空經濟的崛起與具身智能的發展,本質上正在推動 物理世界與智能系統的深度耦合。在這一進化過程中,視頻已經從“呈現內容”轉變為感知輸入與操控反饋的核心數據通道:
視頻不再是給人看的,而是給動作和決策用的。
基於大牛直播SDK構建的超低延遲音視頻鏈路,在採集、編碼、傳輸、解碼、渲染的全路徑上做了工程化優化,補齊了傳統系統的“最後一公里”。它不僅是播放器,更是:
- 遠程指令的閉環起點
- 智能操控系統的視覺神經末梢
- 人與機器之間的實時共感界面
未來,隨着空間計算、邊緣AI、5G/衞星通信持續演進,毫秒級延遲將成為具身智能時代的基礎設施門檻。能否穩定做到低延遲,決定的是:
- 控制系統是否可商用
- 智能體是否真正可執行任務
- 人與機器人是否能實現無違和協同
因此,對於開發者而言,掌握高可靠、低延遲的視頻鏈路技術,不僅是技術選型問題,更是下一代產業競爭力的底層武器。
搶佔實時視頻鏈路,就是提前進入未來的入口。