摘要
本文详细介绍了在 Android 平台上集成 RTSP 和 RTMP 直播播放模块的技术背景、系统要求、准备工作、接口设计、功能支持以及接口调用流程。通过合理的架构设计和优化,开发者可以高效地实现直播播放功能,满足不同场景下的应用需求。
一、技术背景
随着移动互联网的发展,实时视频传输在各个领域的应用越来越广泛。RTSP(Real Time Streaming Protocol)和 RTMP(Real-Time Messaging Protocol)作为主流的视频传输协议,广泛应用于直播、监控等领域。大牛直播 SDK 自 2015 年发布 RTSP 和 RTMP 直播播放模块以来,持续迭代优化,其 SmartPlayer 功能强大、性能强劲、稳定高效,得到了行业内的一致认可。
二、系统要求
-
操作系统:支持 Android 5.1 及以上版本。
-
CPU 架构:支持 armv7、arm64、x86、x86_64。
三、准备工作
在集成 RTSP/RTMP 播放模块之前,需要完成以下准备工作:
1. 文件配置
-
将
SmartPlayerJniV2.java
文件放置在com.daniulive.smartplayer
包名下(可在其他包名下调用)。 -
将
Smartavengine.jar
文件加入到工程中。 -
拷贝
SmartPlayerV2\app\src\main\jniLibs
下的armeabi-v7a
、arm64-v8a
、x86
和x86_64
目录下的libSmartPlayer.so
文件到工程中。
2. 权限配置
在 AndroidManifest.xml
文件中添加以下权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
3. 库加载
在代码中加载相关动态链接库:
static {
System.loadLibrary("SmartPlayer");
}
4. 构建配置
在 build.gradle
文件中配置 32 位和 64 位库:
splits {
abi {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
universalApk true
}
}
5. 应用名称配置
如果需要集成到自己的系统进行测试,可以使用大牛直播 SDK 的 app name,授权版按照授权 app name 正常使用即可。修改 strings.xml
文件中的 app-name:
<string name="app_name">SmartPlayerSDKDemo</string>
四、接口设计
1. 初始化接口
-
SmartPlayerOpen:初始化播放器,设置上下文信息,返回播放实例句柄。
-
SetSmartPlayerEventCallbackV2:设置事件回调接口,用于接收播放器的状态信息。
2. 解码设置接口
-
SetSmartPlayerVideoHWDecoder:设置是否使用 H.264 硬解码播放,如果硬解码不支持,自动适配到软解码。
-
SetSmartPlayerVideoHevcHWDecoder:设置是否使用 H.265 硬解码播放,如果硬解码不支持,自动适配到软解码。
3. 视频设置接口
-
SmartPlayerSetRenderScaleMode:设置视频画面的填充模式,如填充整个 view、等比例填充 view 等。
-
SmartPlayerSetSurfaceRenderFormat:设置 SurfaceView 模式下的 render 类型,如 RGB565 格式或 ARGB8888 格式。
-
SmartPlayerSetSurfaceAntiAlias:设置 SurfaceView 模式下的抗锯齿效果。
4. 播放控制接口
-
SmartPlayerSetSurface:设置播放的 surface,如果为 null,则播放纯音频。
-
SmartPlayerSetHWRenderMode:设置视频硬解码下的 Mediacodec 自行绘制模式。
-
SmartPlayerUpdateHWRenderSurface:更新硬解码 surface。
-
SmartPlayerSetExternalRender:提供解码后的 YUV/RGB 数据接口,供用户自行渲染或进一步处理。
-
SmartPlayerSetExternalAudioOutput:回调 audio 数据到上层,供二次处理之用。
-
SmartPlayerSetAudioOutputType:设置 audio 输出类型,如使用 audiotrack 模式等。
5. 模式设置接口
-
SmartPlayerSetBuffer:设置播放端缓存数据 buffer,单位为毫秒。
-
SmartPlayerSetFastStartup:设置快速启动,实现首屏秒开。
-
SmartPlayerSetLowLatencyMode:设置超低延迟播放模式,延迟可达到 200~400ms。
-
SmartPlayerSwitchPlaybackUrl:快速切换播放 URL,适用于不同数据流之间的快速切换。
6. RTSP 设置接口
-
SmartPlayerSetRTSPTcpMode:设置 RTSP TCP/UDP 模式。
-
SmartPlayerSetRTSPTimeout:设置 RTSP 超时时间,单位为秒。
-
SmartPlayerSetRTSPAutoSwitchTcpUdp:设置 RTSP TCP/UDP 自动切换。
-
SetRTSPAuthenticationInfo:设置 RTSP 用户名和密码。
7. 其他接口
-
SmartPlayerSetMute:实时静音。
-
SmartPlayerSetAudioVolume:实时调节播放音量,范围为 [0, 100]。
-
DisableEnhancedRTMP:禁用 Enhanced RTMP。
-
CaptureImage:支持 JPEG 和 PNG 格式的实时截图。
-
SmartPlayerSetRotation:设置视频镜像旋转,支持 0°、90°、180°、270° 旋转。
-
SmartPlayerSetFlipHorizontal:设置视频水平反转。
-
SmartPlayerSetFlipVertical:设置视频垂直反转。
-
SmartPlayerSetUrl:设置需要播放或录像的 RTMP/RTSP URL。
-
SmartPlayerStartPlay:开始播放 RTSP/RTMP 流。
-
SmartPlayerStopPlay:停止播放 RTSP/RTMP 流。
-
SmartPlayerClose:结束时调用 close 接口释放资源。
五、功能支持
-
音频支持:AAC、Speex(RTMP)、PCMA、PCMU。
-
视频支持:H.264、H.265。
-
播放协议:RTSP、RTMP。
-
播放模式:支持纯音频、纯视频、音视频播放。
-
多实例播放:支持多实例播放。
-
解码支持:支持软解码,特定机型硬解码。
-
RTSP 模式:支持 RTSP TCP、UDP 模式设置及自动切换。
-
超时设置:支持 RTSP 超时时间设置,单位为秒。
-
缓冲设置:支持 buffer 时间设置,单位为毫秒。
-
低延迟模式:支持超低延迟模式。
-
网络处理:支持断网自动重连、视频追赶,支持 buffer 状态等回调。
-
视频操作:支持视频 view 实时旋转(0°、90°、180°、270°)、水平反转、垂直反转。
-
绘制支持:支持 SurfaceView、OpenGL ES、TextureView 绘制。
-
音频模式:支持 AudioTrack、OpenSL ES 模式。
-
截图功能:支持 jpeg、png 实时截图。
-
音量调节:支持实时音量调节。
-
数据回调:支持解码前音视频数据回调、解码后 YUV/RGB 数据回调。
-
RTMP 增强:支持 Enhanced RTMP。
-
录像功能:支持扩展录像功能。
-
系统兼容:支持 Android 5.1 及以上版本。
六、接口调用详解
1. 初始化流程
在 onCreate()
方法中,首先创建 SmartPlayerJniV2
实例:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_smart_player);
libPlayer = new SmartPlayerJniV2();
myContext = this.getApplicationContext();
}
2. 播放控制
实现开始播放和停止播放的功能,调用 InitAndSetConfig()
方法完成常规参数初始化,然后调用播放相关接口:
btnStartStopPlayback.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
if (isPlaying) {
// 停止播放逻辑
} else {
// 开始播放逻辑
if (!isRecording) {
InitAndSetConfig();
}
libPlayer.SmartPlayerSetSurface(playerHandle, sSurfaceView);
libPlayer.SmartPlayerSetRenderScaleMode(playerHandle, 1);
if (isHardwareDecoder && is_enable_hardware_render_mode) {
libPlayer.SmartPlayerSetHWRenderMode(playerHandle, 1);
}
libPlayer.SmartPlayerSetUserDataCallback(playerHandle, new UserDataCallback());
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;
}
// 更新 UI 和状态
}
}
});
3. 事件回调处理
实现事件回调接口,用于处理播放器的状态反馈,如网络状态、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);
}
}
}
4. 录像功能
如果需要对 RTSP/RTMP 流进行录像,可以调用以下接口:
btnStartStopRecorder.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
if (isRecording) {
// 停止录像逻辑
} else {
// 开始录像逻辑
if (!isPlaying) {
InitAndSetConfig();
}
ConfigRecorderFunction();
int startRet = libPlayer.SmartPlayerStartRecorder(playerHandle);
if (startRet != 0) {
Log.e(TAG, "Failed to start recorder.");
return;
}
// 更新 UI 和状态
}
}
});
5. 实时截图
在播放过程中,可以调用以下接口进行实时截图:
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);
}
});
6. 视频操作
对视频 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);
}
}
});
7. 资源释放
在 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 直播播放模块的技术背景、系统要求、准备工作、接口设计、功能支持以及接口调用流程。通过合理的架构设计和优化,开发者可以高效地实现直播播放功能,满足不同场景下的应用需求。希望本文能为开发者提供有价值的参考,助力其实现更加稳定、高效的直播播放应用。