快速开始
1 开发环境配置
- 首先需要搭建好您的业务服务端。搭建好带有 Pili Server sdk 的业务服务端后,SDK 推流信息的输入来自服务端返回的推流地址
- Android Studio 开发工具。官方下载地址
- 下载 Android 官方开发SDK 。官方下载地址。PLDroidMediaStreaming 要求 Android Min API 18
- 下载 PLDroidMediaStreaming 最新的 JAR 和 SO 文件。下载地址
- 请用真机调试代码,模拟器无法调试。
2 创建新工程
-
通过 Android Studio 创建 Project,
-
设置新项目
- 填写 Application id
- 填写 Company Domain
- 填写 Package id
- 选择 Project location
- 可以使用默认的填写项
- 选择 Target Android Devices
本例中选择使用 MinimumSDK API 18
-
选择 Empty Activity
-
填写 Main Activity 信息,作为
android.intent.action.MAIN
-
完成创建
3 导入 SDK
- 将右侧文件夹目录切换为
Project
视图
- 在 app/src/main 目录下创建 jniLibs 目录。按图所示,将文件导入对应的目录。
- 选中 libs 目录下找到 pldroid-media-streaming-3.0.2.jar,右键添加新建库,如图所示
- 导入完成,双击
build.gradle
文件查看内容,lib 目录中的文件已经自动导入,涉及的文件名如下:
// JAR, 以 v3.0.2 为例,实际开发需替换为相应的版本
pldroid-media-streaming-3.0.2.jar
// SO
libpldroid_streaming_aac_encoder.so
libpldroid_streaming_core.so
libpldroid_streaming_h264_encoder.so
libpldroid_mmprocessing.so
libpldroid_streaming_amix.so
libpldroid_streaming_puic.so
4 创建基础推流实例
您可以从这里下载以下代码
4.1 添加相关权限并注册 Activity
- 在 app/src/main 目录中的 AndroidManifest.xml 中增加
uses-permission
和uses-feature
声明,并注册推流 Activity : SWCameraStreamingActivity
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.qiniu.pili.droid.streaming.demo" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />
<application
android:allowBackup="true"
android:name=".StreamingApplication"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_id"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".StreamingByCameraActivity" >
</activity>
</application>
</manifest>
4.2 添加 happy-dns 依赖
- 检查在 app 目录下的
build.gradle
,并且按照如下修改:- 确认在 app 目录下
- 打开如图所示目录文件
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.qiniu.pili.droid.streaming.demo"
minSdkVersion 18
targetSdkVersion 29
versionCode 1
versionid "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
// 推流 SDK jar 包,为推流 SDK 必须依赖的库
implementation files('libs/pldroid-media-streaming-3.0.2.jar')
// dns 相关 jar 包,必须依赖
implementation 'com.qiniu:happy-dns:0.2.17'
// 监听应用生命周期,必须依赖
implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation 'com.android.support:appcompat-v7:28.0.0'
}
其中,不同版本的 SDK 需依赖的 HappyDns 版本可能不同,详情可参阅版本升级须知
4.3 实现自己的 Application
public class StreamingApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
StreamingEnv.init(getApplicationContext(), Util.getUserId(getApplicationContext()));
}
}
其中,StreamingEnv.init
的第二个参数代表用户唯一标识符,用于区分不同的用户
4.4 创建主界面
- 查看 app/src/main/java 目录中的 MainActivity.java 文件。为了演示方便,将文件中的
MainActivity
父类AppCompatActivity
更改为Activity
,即public class MainActivity extends AppCompatActivity
,修改为public class MainActivity extends Activity
,MainActivity
其主要工作包括: - 通过 start Button 去 app server 异步请求推流地址
- 推流地址获取成功之后,启动
SWCameraStreamingActivity
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private String requestPublishURL() {
try {
// Replace "Your app server" by your app sever url which can get the publish URL as the SDK's input.
HttpURLConnection httpConn = (HttpURLConnection) new URL("Your app server").openConnection();
httpConn.setRequestMethod("POST");
httpConn.setConnectTimeout(5000);
httpConn.setReadTimeout(10000);
int responseCode = httpConn.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
return null;
}
int length = httpConn.getContentLength();
if (length <= 0) {
return null;
}
InputStream is = httpConn.getInputStream();
byte[] data = new byte[length];
int read = is.read(data);
is.close();
if (read <= 0) {
return null;
}
return new String(data, 0, read);
} catch (Exception e) {
showToast("Network error!");
}
return null;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = (Button) findViewById(R.id.start);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
try {
// Get the publish URL from http
String publishURL = requestPublishURL();
Intent intent = new Intent(MainActivity.this, StreamingByCameraActivity.class);
intent.putExtra("stream_publish_url", publishURL);
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
});
}
}
4.5 创建主界面布局文件
- 查看 app/src/main/res/layout 目录下的
activity_main.xml
- 底部 TAB 切换至 Text 面板,粘贴如下代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<Button
android:id="@+id/start"
android:text="start"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
4.6 创建推流界面
-
创建名为
SWCameraStreamingActivity
的 Empty Activity,SWCameraStreamingActivity
的主要工作包括:SWCameraStreamingActivity
获取MainActivity
从 app server 获取到的推流地址- 在
onCreate
中初始化推流的配置以及推流 SDK 的核心类MediaStreamingManager
- 在
onResume
中调用mMediaStreamingManager.resume();
- 在接收到
READY
之后,开始推流mMediaStreamingManager.startStreaming();
,startStreaming
需要在非 UI 线程中进行操作。
-
在 app/src/main/java 目录下创建
StreamingByCameraActivity
文件,代码如下:
public class StreamingByCameraActivity extends Activity
implements StreamingStateChangedListener, StreamStatusCallback, AudioSourceCallback, StreamingSessionListener {
CameraPreviewFrameView mCameraPreviewSurfaceView;
private MediaStreamingManager mMediaStreamingManager;
private StreamingProfile mProfile;
private String TAG = "StreamingByCameraActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera_streaming);
init();
}
private void init() {
//get form you server
String publishURLFromServer = "rtmpp://xxxx/xx/x";
mCameraPreviewSurfaceView = findViewById(R.id.cameraPreview_surfaceView);
try {
//encoding setting
mProfile = new StreamingProfile();
mProfile.setVideoQuality(StreamingProfile.VIDEO_QUALITY_HIGH1)
.setAudioQuality(StreamingProfile.AUDIO_QUALITY_MEDIUM2)
.setEncodingSizeLevel(StreamingProfile.VIDEO_ENCODING_HEIGHT_480)
.setEncoderRCMode(StreamingProfile.EncoderRCModes.QUALITY_PRIORITY)
.setPublishUrl(publishURLFromServer);
//preview setting
CameraStreamingSetting camerasetting = new CameraStreamingSetting();
camerasetting.setCameraId(Camera.CameraInfo.CAMERA_FACING_BACK)
.setContinuousFocusModeEnabled(true)
.setCameraPrvSizeLevel(CameraStreamingSetting.PREVIEW_SIZE_LEVEL.MEDIUM)
.setCameraPrvSizeRatio(CameraStreamingSetting.PREVIEW_SIZE_RATIO.RATIO_16_9);
//streaming engine init and setListener
mMediaStreamingManager = new MediaStreamingManager(this, mCameraPreviewSurfaceView, AVCodecType.SW_VIDEO_WITH_SW_AUDIO_CODEC); // soft codec
mMediaStreamingManager.prepare(camerasetting, mProfile);
mMediaStreamingManager.setStreamingStateListener(this);
mMediaStreamingManager.setStreamingSessionListener(this);
mMediaStreamingManager.setStreamStatusCallback(this);
mMediaStreamingManager.setAudioSourceCallback(this);
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
@Override
public void onStateChanged(StreamingState streamingState, Object extra) {
Log.e(TAG, "streamingState = " + streamingState + "extra = " + extra);
switch (streamingState) {
case PREPARING:
Log.e(TAG, "PREPARING");
break;
case READY:
Log.e(TAG, "READY");
// start streaming when READY
new Thread(new Runnable() {
@Override
public void run() {
if (mMediaStreamingManager != null) {
mMediaStreamingManager.startStreaming();
}
}
}).start();
break;
case CONNECTING:
Log.e(TAG, "连接中");
break;
case STREAMING:
Log.e(TAG, "推流中");
// The av packet had been sent.
break;
case SHUTDOWN:
Log.e(TAG, "直播中断");
// The streaming had been finished.
break;
case IOERROR:
// Network connect error.
Log.e(TAG, "网络连接失败");
break;
case OPEN_CAMERA_FAIL:
Log.e(TAG, "摄像头打开失败");
// Failed to open camera.
break;
case DISCONNECTED:
Log.e(TAG, "已经断开连接");
// The socket is broken while streaming
break;
case TORCH_INFO:
Log.e(TAG, "开启闪光灯");
break;
}
}
@Override
protected void onResume() {
super.onResume();
mMediaStreamingManager.resume();
}
@Override
protected void onPause() {
super.onPause();
// You must invoke pause here.
mMediaStreamingManager.pause();
}
@Override
public void notifyStreamStatusChanged(StreamingProfile.StreamStatus status) {
Log.e(TAG, "StreamStatus = " + status);
}
@Override
public void onAudioSourceAvailable(ByteBuffer srcBuffer, int size, long tsInNanoTime, boolean isEof) {
}
@Override
public boolean onRecordAudioFailedHandled(int code) {
Log.i(TAG, "onRecordAudioFailedHandled");
return false;
}
@Override
public boolean onRestartStreamingHandled(int code) {
Log.i(TAG, "onRestartStreamingHandled");
new Thread(new Runnable() {
@Override
public void run() {
if (mMediaStreamingManager != null) {
mMediaStreamingManager.startStreaming();
}
}
}).start();
return false;
}
@Override
public void onBackPressed() {
super.onBackPressed();
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
@Override
public Camera.Size onPreviewSizeSelected(List<Camera.Size> list) {
return null;
}
@Override
public int onPreviewFpsSelected(List<int[]> list) {
return -1;
}
4.7 创建推流界面布局文件
- 查看 app/src/main/res/layout 中的
activity_swcamera_streaming.xml
- 切换至 Text 面板,粘贴如下内容
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background_floating_material_dark"
tools:context=".SWCameraStreamingActivity" >
<com.qiniu.pili.droid.streaming.demo.ui.CameraPreviewFrameView
android:id="@+id/cameraPreview_surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
</RelativeLayout>
启动 APP 之后,当点击 start button,就可以开始推流了。
4.8 测试播放效果
- 测试方法: 从 app server 获取到推流对应的播放地址,输入到播放器中进行播放。
5 从 SDK DEMO 开始使用
5.1 下载 SDK DEMO
- 从这里下载 SDK Demo。
5.2 导入 Project 到 Android Studio
-
启动 Android Studio 并选择
Open an existing Android Studio project
。
-
选择 PLDroidCameraStreamingDemo 工程。从目录
~/workdir/workspace/sdk/PLDroidCameraStreaming-1.4.1/
中选择DroidCameraStreamingDemo
工程,选择完成后,点击Choose
按钮。
-
恭喜你,导入完成。
5.3 已有工程导入 SDK
-
SDK 下载地址。
-
Demo Project 目录结构说明。如下图所示:
- 红色框:是 Demo 依赖 SDK 的 JAR 文件。您也可以在 build.gradle 中自定义其路径:
dependencies { // 推流 SDK jar 包,为推流 SDK 必须依赖的库 implementation files('libs/pldroid-media-streaming-3.0.2.jar') // dns 相关 jar 包,必须依赖 implementation 'com.qiniu:happy-dns:0.2.17' // 监听应用生命周期,必须依赖 implementation 'android.arch.lifecycle:extensions:1.1.1' implementation 'com.android.support:appcompat-v7:28.0.0' }
-
绿色框:是 Demo Java 代码部分。
- AudioStreamingActivity 是纯音频推流的样例代码
- AVStreamingActivity 是音视频推流的样例代码
- ImportStreamingActivity 是外部导入推流的样例代码
- ScreenStreamingActivity 是录屏推流的样例代码 -
橙色框:是 Demo 依赖 SDK 的动态链接库文件。目前 SDK 支持主流的 ARM, ARMv7a, ARM64v8a, X86 芯片体系架构。