这篇文章介绍了如何用Wowza Gocoder SDK Android版开发一个基本功能的直播APP。
一、创建一个新的开发项目
首先你需要在Android Studio中创建一个APP开发项目。
二、添加一个直播按钮
在这一步,你需要在APP的界面上添加一个直播按钮.
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">
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/camera_preview"
wowza:scaleMode="fill"
wowza:defaultCamera="back"
wowza:frameSizePreset="frameSize1280x720"/>
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" />
在 [project_root]/app/src/main/AndroidManifest.xml 文件中,为 activity 元素添加 android:configChanges 参数。
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定义它所需要的权限:
?
xmlns:android="http://schemas.android.com/apk/res/android"
?
package="com.cintimedia.foobar">
对于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;
?
}
六、开启摄像头预览
接下来,我们需要配置和开启摄像头预览:
?
// 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();
?
// 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);
}
}
?
?
// 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都有它的用武之地。