這篇文章介紹了如何用Wowza Gocoder SDK Android版開發一個基本功能的直播APP。
一、創建一個新的開發項目
首先你需要在Android Studio中創建一個APP開發項目。
打開Android Studio,在 Welcome to Android Studio 窗口點擊 Start a New Android Studio Project
在 Create New Project 窗口中的 New Project 界面上點擊 Edit 編輯 Package Name 的名字。 然後輸入在生成SDK License Key時提交的應用類包名,然後點擊 Next 。
在 project options的對話框中,輸入 Product Name 和 Organization Name 。 注意, Bundle Identifier 必須與申請 license key時提交的一樣。
在 Target Android Devices 界面,選擇APP運行的設備類型(通常是手機或者平板),選擇支持的最低系統版本API 19及以上。然後點擊 Next
在 Add an Activity to Mobile 界面選擇 Empty Activity ,然後點擊 Next
在 Customize the Activity 界面,接受默認設置,然後點擊 Finish
確認Gocoder SDK 的類庫被安裝正確,並在 AndroidManifest.xml 文件中添加的權限。
二、添加一個直播按鈕
在這一步,你需要在APP的界面上添加一個直播按鈕.
在Android Studio上點擊 File ,然後點擊 Open
打開 [project_root]/app/src/main/res/layout/activity_main.xml
將下面的配置更新到 activity_main.xml 文件中,並將tools:context參數的com.mycompany.myapp替換為當前APP的 MainActivity Class:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:wowza="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.mycompany.myapp.MainActivity">
<!-- The camera preview display -->
<com.wowza.gocoder.sdk.api.devices.WZCameraView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/camera_preview"
wowza:scaleMode="fill"
wowza:defaultCamera="back"
wowza:frameSizePreset="frameSize1280x720"/>
<!-- The broadcast button -->
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Broadcast"
android:id="@+id/broadcast_button"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
在 [project_root]/app/src/main/AndroidManifest.xml 文件中,為 activity 元素添加 android:configChanges 參數。
<activity android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize">
為 MainActivity Class添加 onWindowFocusChanged() 方法,以此來開啟Android的immersive full-screen 模式。
//
// Enable Android's sticky immersive full-screen mode
//
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
View rootView = getWindow().getDecorView().findViewById(android.R.id.content);
if (rootView != null)
rootView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
三、定義APP的參數
在 MainActivity.java 文件中為 MainActivity Class 添加下面的成員變量:
public class MainActivity extendsAppCompatActivity {
// The top level GoCoder API interface
private WowzaGoCoder goCoder;
// The GoCoder SDK camera view
private WZCameraView goCoderCameraView;
// The GoCoder SDK audio device
private WZAudioDevice goCoderAudioDevice;
// The broadcast configuration settings
private WZBroadcastConfig goCoderBroadcastConfig;
// Properties needed for Android 6+ permissions handling
private static final intPERMISSIONS_REQUEST_CODE = 0x1;
private boolean mPermissionsGranted = true;
private String[] mRequiredPermissions = new String[] {
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
};
四、註冊和初始化SDK
在這一步,你需要在 MainActivity Class的 onCreate() 方法中添加下面的代碼來註冊和初始化GoCoder SDK 的License。
請用你的License key替換下面的 GOSK-XXXX-XXXX-XXXX-XXXX-XXXX 。
// Initialize the GoCoder SDK
goCoder =WowzaGoCoder.init(getApplicationContext(), "GOSK-XXXX-XXXX-XXXX-XXXX-XXXX");
if (goCoder == null) {
// If initialization failed, retrieve the last error and display it
WZError goCoderInitError = WowzaGoCoder.getLastError();
Toast.makeText(this,
"GoCoder SDK error: " +goCoderInitError.getErrorDescription(),
Toast.LENGTH_LONG).show();
return;
}
五、檢查APP的權限
接下來,你必須為APP定義它所需要的權限:
在頂級的 manifest 元素下方的 [project_root]/app/src/main/AndroidManifest.xml 文件中添加下面的權限:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cintimedia.foobar">
<!-- Define the required application permissions -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
對於Android 6.0及以上版本,你需要在 MainActivity class中添加 onResume() 和 onRequestPermissionsResult() 方法來檢查用戶已經授權APP來訪問攝像頭和麥克風。
//
// Called when an activity is brought to the foreground
//
@Override
protected void onResume() {
super.onResume();
// If running on Android 6 (Marshmallow) or above, check to see if the necessary permissions
// have been granted
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mPermissionsGranted = hasPermissions(this, mRequiredPermissions);
if (!mPermissionsGranted)
ActivityCompat.requestPermissions(this, mRequiredPermissions, PERMISSIONS_REQUEST_CODE);
} else
mPermissionsGranted = true;
}
//
// Callback invoked in response to a call to ActivityCompat.requestPermissions() to interpret
// the results of the permissions request
//
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
mPermissionsGranted = true;
switch (requestCode) {
case PERMISSIONS_REQUEST_CODE: {
// Check the result of each permission granted
for(int grantResult : grantResults) {
if (grantResult != PackageManager.PERMISSION_GRANTED) {
mPermissionsGranted = false;
}
}
}
}
}
//
// Utility method to check the status of a permissions request for an array of permission identifiers
//
private static boolean hasPermissions(Context context, String[] permissions) {
for(String permission : permissions)
if (context.checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_GRANTED)
return false;
return true;
}
六、開啟攝像頭預覽
接下來,我們需要配置和開啟攝像頭預覽:
在 MainActivity Class的 onCreate() 方法中添加下面的代碼,將在activity的layout中定義的視圖(view)與 WZCameraView 關聯起來,並創建一個音頻輸入設備實例。
// Associate the WZCameraView defined in the U/I layout with the corresponding class member
goCoderCameraView = (WZCameraView) findViewById(R.id.camera_preview);
// Create an audio device instance for capturing and broadcasting audio
goCoderAudioDevice = new WZAudioDevice();
在 Main Activity Class的 onResume() 方法的最後面添加以下代碼(在權限檢查邏輯之後),當APP喚醒到前臺時,來開啟攝像頭預覽
// Start the camera preview display
if (mPermissionsGranted && goCoderCameraView != null) {
if (goCoderCameraView.isPreviewPaused())
goCoderCameraView.onResume();
else
goCoderCameraView.startPreview();
}
七、配置直播相關參數
在 MainActivity Class的 onCreate 方法中加入對直播的相關參數,包括伺服器地址(hostAddress)、埠(portNumber)、 應用名(applicationName)和流名(streamName)。這個伺服器不限於Wowza Streaming Engine或Wowza Streaming Cloud。
如果你要往Wowza Streaming Cloud 雲平臺推流,那麼你可以在Wowza Streaming Cloud的Web界面的live streaming詳情的 Overview Tab頁上找到 Connection Code
// Get a copy of the active config
WowzaConfig *goCoderBroadcastConfig =self.goCoder.config;
// Set the defaults for 720p video
[goCoderBroadcastConfigloadPreset:WZFrameSizePreset1280x720];
// Set the connection properties for thetarget Wowza Streaming Engine server or Wowza Cloud account
goCoderBroadcastConfig.hostAddress =@"live.someserver.net";
goCoderBroadcastConfig.portNumber =1935;
goCoderBroadcastConfig.applicationName =@"live";
goCoderBroadcastConfig.streamName =@"myStream";
// Update the active config
self.goCoder.config =goCoderBroadcastConfig;
八、添加流傳輸的狀態回調
在這一步,我們添加對流傳輸狀態監控的回調。
1、我們首先要在primary activity( MainActivity )添加WZStatusCallback接口,用來對直播流的傳輸做狀態監控:
// Main app activity class
public class MainActivity extends AppCompatActivity
implements WZStatusCallback {
2、在 MainActivity Class中添加WZStatusCallback接口中定義的方法:
//
// The callback invoked upon changes tothe state of the steaming broadcast
//
@Override
public void onWZStatus(final WZStatusgoCoderStatus) {
// A successful status transition has beenreported by the GoCoder SDK
final StringBuffer statusMessage = newStringBuffer("Broadcast status: ");
switch (goCoderStatus.getState()) {
case WZState.STARTING:
statusMessage.append("Broadcast initialization");
break;
case WZState.READY:
statusMessage.append("Ready to begin streaming");
break;
case WZState.RUNNING:
statusMessage.append("Streaming is active");
break;
case WZState.STOPPING:
statusMessage.append("Broadcast shutting down");
break;
case WZState.IDLE:
statusMessage.append("The broadcast is stopped");
break;
default:
return;
}
// Display the status message using the U/Ithread
new Handler(Looper.getMainLooper()).post(newRunnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, statusMessage,Toast.LENGTH_LONG).show();
}
});
}
//
// The callback invoked when an erroroccurs during a broadcast
//
@Override
public void onWZError(final WZStatusgoCoderStatus) {
// If an error is reported by the GoCoder SDK,display a message
// containing the error details using the U/Ithread
new Handler(Looper.getMainLooper()).post(newRunnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,
"Streaming error: " +goCoderStatus.getLastError().getErrorDescription(),
Toast.LENGTH_LONG).show();
}
});
}
九、開始直播
在開始直播之前,您還需要為直播按鈕加上啟動和停止直播的代碼:
在 MainActivity Class中添加 View.onClickListener 接口:
// Main app activity class
public class MainActivity extends AppCompatActivity
implements WZStatusCallback, View.OnClickListener {
在 MainActivity Class的 onCreate() 方法的最底部,它在直播按鈕被按下時被調用:
//
// The callback invoked when the broadcast button is pressed
//
@Override
public void onClick(View view) {
// return if the user hasn't granted the app the necessary permissions
if (!mPermissionsGranted) return;
// Ensure the minimum set of configuration settings have been specified necessary to
// initiate a broadcast streaming session
WZStreamingError configValidationError = goCoderBroadcastConfig.validateForBroadcast();
if (configValidationError != null) {
Toast.makeText(this, configValidationError.getErrorDescription(), Toast.LENGTH_LONG).show();
} else if (goCoderBroadcaster.getStatus().isRunning()) {
// Stop the broadcast that is currently running
goCoderBroadcaster.endBroadcast(this);
} else {
// Start streaming
goCoderBroadcaster.startBroadcast(goCoderBroadcastConfig, this);
}
}
在 MainActivity Class的 onCreate() 方法中添加下面的代碼將 onClick() 事件處理和activity layout中定義的直播按鈕關聯上,
// Associate the onClick() method as the callback for the broadcast button's click event
Button broadcastButton = (Button) findViewById(R.id.broadcast_button);
broadcastButton.setOnClickListener(this);
十、構建和運行你的APP
最後,可以編譯和測試的APP了,請點擊 Run 菜單,選擇 Run 或者 Debug 。
十一、MainActivity的例子程序
下面是一個完整的 MainActivity Class的例子,代碼包括上面講到的所有內容:
public class MainActivity extendsAppCompatActivity
implements WZStatusCallback, View.OnClickListener {
// The top level GoCoder API interface
private WowzaGoCoder goCoder;
// The GoCoder SDK camera view
private WZCameraView goCoderCameraView;
// The GoCoder SDK audio device
private WZAudioDevice goCoderAudioDevice;
// The GoCoder SDK broadcaster
private WZBroadcast goCoderBroadcaster;
// The broadcast configuration settings
private WZBroadcastConfig goCoderBroadcastConfig;
// Properties needed for Android 6+ permissions handling
private static final int PERMISSIONS_REQUEST_CODE = 0x1;
private boolean mPermissionsGranted = true;
private String[] mRequiredPermissions = new String[] {
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize the GoCoder SDK
goCoder = WowzaGoCoder.init(getApplicationContext(),"GOSK-XXXX-XXXX-XXXX-XXXX-XXXX");
if (goCoder == null) {
// If initialization failed, retrieve the last error and display it
WZError goCoderInitError = WowzaGoCoder.getLastError();
Toast.makeText(this,
"GoCoder SDK error: " +goCoderInitError.getErrorDescription(),
Toast.LENGTH_LONG).show();
return;
}
// Associate the WZCameraView defined in the U/I layout with thecorresponding class member
goCoderCameraView = (WZCameraView) findViewById(R.id.camera_preview);
// Create an audio device instance for capturing and broadcasting audio
goCoderAudioDevice = new WZAudioDevice();
// Create a broadcaster instance
goCoderBroadcaster = new WZBroadcast();
// Create a configuration instance for the broadcaster
goCoderBroadcastConfig= new WZBroadcastConfig(WZMediaConfig.FRAME_SIZE_1920x1080);
// Set the connection properties for the target Wowza Streaming Engineserver or Wowza Cloud account
goCoderBroadcastConfig.setHostAddress("live.someserver.net");
goCoderBroadcastConfig.setPortNumber(1935);
goCoderBroadcastConfig.setApplicationName("live");
goCoderBroadcastConfig.setStreamName("myStream");
// Designate the camera preview as the video broadcaster
goCoderBroadcastConfig.setVideoBroadcaster(goCoderCameraView);
// Designate the audio device as the audio broadcaster
goCoderBroadcastConfig.setAudioBroadcaster(goCoderAudioDevice);
// Associate the onClick() method as the callback for the broadcastbutton's click event
Button broadcastButton = (Button) findViewById(R.id.broadcast_button);
broadcastButton.setOnClickListener(this);
}
//
// Called when an activity is brought to the foreground
//
@Override
protected void onResume() {
super.onResume();
// If running on Android 6 (Marshmallow) or above, check to see if thenecessary permissions
// have been granted
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mPermissionsGranted = hasPermissions(this, mRequiredPermissions);
if (!mPermissionsGranted)
ActivityCompat.requestPermissions(this, mRequiredPermissions,PERMISSIONS_REQUEST_CODE);
} else
mPermissionsGranted = true;
// Start the camera preview display
if (mPermissionsGranted && goCoderCameraView != null) {
if (goCoderCameraView.isPreviewPaused())
goCoderCameraView.onResume();
else
goCoderCameraView.startPreview();
}
}
//
// Callback invoked in response to a call toActivityCompat.requestPermissions() to interpret
// the results of the permissions request
//
@Override
public void onRequestPermissionsResult(int requestCode, Stringpermissions[], int[] grantResults) {
mPermissionsGranted = true;
switch (requestCode) {
case PERMISSIONS_REQUEST_CODE: {
// Check the result of each permission granted
for(int grantResult : grantResults) {
if (grantResult != PackageManager.PERMISSION_GRANTED) {
mPermissionsGranted = false;
}
}
}
}
}
//
// Utility method to check the status of a permissions request for anarray of permission identifiers
//
private static boolean hasPermissions(Context context, String[]permissions) {
for(String permission : permissions)
if (context.checkCallingOrSelfPermission(permission) !=PackageManager.PERMISSION_GRANTED)
return false;
return true;
}
//
// The callback invoked when the broadcast button is pressed
//
@Override
public void onClick(View view) {
//return if the user hasn't granted the app the necessary permissions
if (!mPermissionsGranted) return;
// Ensure the minimum set of configuration settings have been specifiednecessary to
// initiate a broadcast streaming session
WZStreamingError configValidationError =goCoderBroadcastConfig.validateForBroadcast();
if (configValidationError != null) {
Toast.makeText(this, configValidationError.getErrorDescription(),Toast.LENGTH_LONG).show();
} else if (goCoderBroadcaster.getStatus().isRunning()) {
// Stop the broadcast that is currently running
goCoderBroadcaster.endBroadcast(this);
} else {
// Start streaming
goCoderBroadcaster.startBroadcast(goCoderBroadcastConfig, this);
}
}
//
// The callback invoked upon changes to the state of the steamingbroadcast
//
@Override
public void onWZStatus(final WZStatus goCoderStatus) {
// A successful status transition has been reported by the GoCoder SDK
final StringBuffer statusMessage = new StringBuffer("Broadcaststatus: ");
switch (goCoderStatus.getState()) {
case WZState.STARTING:
statusMessage.append("Broadcast initialization");
break;
case WZState.READY:
statusMessage.append("Ready to begin streaming");
break;
case WZState.RUNNING:
statusMessage.append("Streaming is active");
break;
case WZState.STOPPING:
statusMessage.append("Broadcast shutting down");
break;
case WZState.IDLE:
statusMessage.append("The broadcast is stopped");
break;
default:
return;
}
// Display the status message using the U/I thread
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, statusMessage,Toast.LENGTH_LONG).show();
}
});
}
//
// The callback invoked when an error occurs during a broadcast
//
@Override
public void onWZError(final WZStatus goCoderStatus) {
// If an error is reported by the GoCoder SDK, display a message
// containing the error details using the U/I thread
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,
"Streaming error: " +goCoderStatus.getLastError().getErrorDescription(),
Toast.LENGTH_LONG).show();
}
});
}
//
// Enable Android's sticky immersive full-screen mode
//
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
View rootView =getWindow().getDecorView().findViewById(android.R.id.content);
if (rootView != null)
rootView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
}
Wowza Streaming Engine 4是業界功能強大、API接口豐富的流媒體Server產品,採用它作為流媒體伺服器產品的案例很多,直播、在線教育、IPTV都有它的用武之地。
公司名稱:北京哲想軟體有限公司
北京哲想軟體官方網站:www.cogitosoft.com
北京哲想軟體微信公眾平臺帳號:cogitosoftware
北京哲想軟體微博:哲想軟體
北京哲想軟體郵箱:sales@cogitosoft.com
銷售(俞先生)聯繫方式:+86(010)68421378
微信:18610247936 QQ:368531638