Skip to content
BoleLiu edited this page Aug 7, 2020 · 4 revisions

4 快速开始

4.1 开发环境配置

  • 已全部完成 BOOK - I 中的所有操作。搭建好带有 Pili server sdk 的业务服务端,SDK 推流信息的输入来自服务端返回的推流地址或者 StreamJson(推荐使用推流地址
  • Android Studio 开发工具。官方下载地址
  • 下载 Android 官方开发SDK 。官方下载地址。PLDroidMediaStreaming 软编要求 Min API 15 和硬编要求 Android Min API 18
  • 下载 PLDroidMediaStreaming 最新的 JAR 和 SO 文件。下载地址
  • 请用真机调试代码,模拟器无法调试。

4.2 创建新工程

  • 通过 Android Studio 创建 Project, 创建新工程

  • 设置新项目

    • 填写 Application id
    • 填写 Company Domain
    • 填写 Package id
    • 选择 Project location
    • 可以使用默认的填写项

设置新项目

  • 选择 Target Android Devices

本例中选择使用 MinimumSDK API 18(软编要求 MinimumSDK API 15; 硬编要求 MinimumSDK API 18) MinimumSDK API 18

  • 选择 Empty Activity 选择 Empty Activity

  • 填写 Main Activity 信息,作为 android.intent.action.MAIN 填写 Main Activity 信息

  • 完成创建 完成创建

4.3 导入 SDK

  • 将右侧文件夹目录切换为 Project 视图

Project

  • 在 app/src/main 目录下创建 jniLibs 目录。按图所示,将文件导入对应的目录。

导入路径

  • 选中 lib 目录下 pldroid-media-streaming-2.4.0.jar,右键添加新建库,如图所示

添加新库

  • 导入完成,双击 build.gradle 文件查看内容,lib 目录中的文件已经自动导入,涉及的文件名如下:
// JAR, 以 v2.4.0 为例,实际开发需替换为相应的版本
pldroid-media-streaming-2.4.0.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 创建基础播放实例

4.4.1 添加相关权限并注册 Activity

  • 在 app/src/main 目录中的 AndroidManifest.xml 中增加 uses-permissionuses-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.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-feature android:name="android.hardware.camera.autofocus" />
    <uses-feature android:glEsVersion="0x00020000" android:required="true" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

    <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.4.2 添加 happy-dns 依赖

  • 检查在 app 目录下的 build.gradle ,并且按照如下修改:
    • 确认在 app 目录下
    • 打开如图所示目录文件
apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {
        applicationId "com.qiniu.pili.droid.streaming.demo"
        minSdkVersion 18
        targetSdkVersion 22
        versionCode 1
        versionid "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.qiniu:happy-dns:0.2.16'
    compile 'com.android.support:appcompat-v7:22.0.0'
    compile files('libs/pldroid-camera-streaming-2.1.0.jar')
}

4.4.3 实现自己的 Application

public class StreamingApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        StreamingEnv.init(getApplicationContext());
    }
}

4.4.4 创建主界面

  • 查看 app/src/main/java 目录中的 MainActivity.java 文件。为了演示方便,将文件中的 MainActivity 父类 AppCompatActivity 更改为 Activity,即 public class MainActivity extends AppCompatActivity,修改为 public class MainActivity extends ActivityMainActivity 其主要工作包括:
  • 通过 start Button 去 app server 异步请求推流地址或者 stream Json(注:当前推荐使用推流地址进行推流,stream Json 逐渐会被废弃
  • 推流地址获取成功之后,启动 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.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.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.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.4.8 测试播放效果

  • 测试方法: 从 app server 获取到推流对应的播放地址,输入到播放器中进行播放。

4.5 从 SDK DEMO 开始使用

4.5.1 下载 SDK DEMO

  • 这里下载 SDK Demo。

  • 假设下载的是 v1.4.1 版本的 PLDroidCameraStreaming-1.4.1.zip,解压 zip 包:

$ unzip -n PLDroidCameraStreaming-1.4.1.zip -d ~/workdir/workspace/sdk/

或者解压到您的工作目录下。

4.5.2 导入 Project 到 Android Studio

  • 启动 Android Studio 并选择 Open an existing Android Studio projectAndroid Studio 打开

  • 选择 PLDroidCameraStreamingDemo 工程。从目录 ~/workdir/workspace/sdk/PLDroidCameraStreaming-1.4.1/ 中选择 DroidCameraStreamingDemo 工程,选择完成后,点击 Choose 按钮。 DroidCameraStreamingDemo

  • 恭喜你,导入完成。 完成

4.5.3 已有工程导入 SDK

  • SDK 下载地址
  • Demo Project 目录结构说明。如下图所示:
    • 红色框:是 Demo 依赖 SDK 的 JAR 文件。您也可以在 build.gradle 中自定义其路径:
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.qiniu:happy-dns:0.2.7'
        compile files('libs/pldroid-camera-streaming-1.4.1.jar')
      }
    • 绿色框:是 Demo Java 代码部分。

      • HWCodecCameraStreamingActivity 是硬编的样例代码;
      • SWCodecCameraStreamingActivity 是软编的样例代码;
      • AudioStreamingActivity 是纯音频的样例代码
    • 橙色框:是 Demo 依赖 SDK 的动态链接库文件。目前 SDK 支持主流的 ARM, ARMv7a, ARM64v8a, X86 芯片体系架构。

    • 蓝色框:是 Demo 的布局文件。 demo