How to Build a Simple iOS or Android Live Mobile Streaming App

Module 1: Publishing

This module includes step by step instructions for building both iOS and Android mobile live streaming apps capable of delivering live streams from devices to a cloud streaming service or streaming media server.

iOS: Building a Simple iOS Mobile Streaming App

In this secton, we will cover the steps necessary to build a simple iOS mobile streaming application using Xcode and the Wowza GoCoder SDK. This will enable iOS devices to:

  • Capture video and audio.
  • Encode the content.
  • Publish the content to a live-streaming workflow.

The Wowza GoCoder SDK and its associated documentation enable you to turn iOS and Android devices into professional-grade streaming cameras and encoders.

Note: If you wish to only build an Android version and skip building an iOS app, go to the next section: Building a Simple Android Mobile Streaming Application.

Don’t want to build your own mobile streaming application? The Wowza branded version of the GoCoder application is available free of charge on the AppStore and GooglePlay for use with Wowza Streaming Cloud. With this option, you can skip ahead to Module 2: Processing.

Before you begin, you will need:

  • An iOS device with OS version 8.01 or newer.
  • An active Wowza Streaming Cloud plan.
  • An active license of the Wowza GoCoder SDK (with an iOS Bundle Identifier).
  • Xcode 7 or newer.

Creating an iOS App Project and Installing / Configuring GoCoder SDK

There are two main steps involved in building an iOS streaming app, which we’ll cover below: creating an app project, and installing and configuring GoCoder SDK.

Step 1: Creating an iOS App Project

In this step, we are creating an application project to attach and build upon functionality within the GoCoder SDK that will turn an iOS device into a streaming camera and encoder.

Begin by creating an app project in Xcode. From within Xcode:

  1. Click File, click New and then select Project.
  2. In the template selection dialog, select Single View Application and then click Next.
  3. In the project options dialog box, enter a Product Name and Organization Name. IMPORTANT: The Bundle Identifier must match the identifier used to generate your SDK license key.
  4. Click Next, specify where you want your source repository to be located and then click Create.

Installing/Adding the iOS GoCoder SDK to Your Project

Now that a project has been created, the next step is to mount the GoCoder SDK within the project:

  1. Download the current release ofWowza GoCoder SDK for iOS.
  2. Expand the downloaded .zip file, which contains the following files and folders:

/docs

The SDK API reference documentation in HTML format. Open index.html in this folder to view the documentation locally.

/ModuleGoCoderSDKSample

Sample code that demonstrates integration between Wowza Streaming Engine™ media server software and the SDK.

ReleaseNotes.md

A markdown file that contains release notes.

/sample-apps

A set of Xcode projects that demonstrate many of the SDK’s capabilities.

/wowzagocoder_static_lib

A static library version of the GoCoder SDK library for Objective-C applications.

WowzaGoCoderSDK.framework

A dynamic framework version of the GoCoder SDK library that supports both Objective-C and Swift-based application development.

  1. With the project selected in the navigator area of the Xcode main window, click the File menu and choose Add Files to [project name].
  2. Navigate to and select WowzaGoCoderSDK.framework in the expanded .zip that you downloaded.
  3. Under Destination, select Copy items if needed.
  4. Under Add to targets, select your project.
  5. Click Add.
  6. In the editor area of the main window, under the General tab, scroll down to Embedded Binaries and click the + (add) button.
  7. Select WowzaGoCoderSDK.framework, and then click Add.
  8. Add an umbrella statement to your project’s .h or .swift source files so they can reference GoCoder SDK classes.
  9. In the navigator area, locate your project’s AppDelegate and ViewController .h or .swift files.
  10. Copy one of the following statements into the top of the files, below the commented-out copyright information:

Swift: import WowzaGoCoderSDK

Objective-C: #import <WowzaGoCoderSDK/WowzaGoCoderSDK.h>

Step 2A (Optional): Removing Inactive Code From the Project

If you are intending to submit your app to the Apple App Store, you will want to remove some of the inactive code from the binary. The Wowza GoCoder SDK framework contains both ARM and x86 code, which allows you to use it on a device or in the simulator. To accomplish this, run the following bash script to strip the inactive code from the binary.

  1. In the editor area of the project’s main window, click Build Phases.
  2. Click the + (add) button in the upper-left corner of the Build Phases editor.

Xcode adds a Run Script build phase to the list.

  1. Expand the Run Script build phase and paste the following into the script editor field:

bash “${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/WowzaGoCoderSDK.framework/strip-frameworks.sh”

Note: Running and debugging apps that use the GoCoder SDK using a device emulator is not recommended due to the wide variance in functionality between the software-based audio and video codecs used by the emulator and the hardware-based codecs installed on most devices.

At this point, you can begin building up on the SDK in your application project.

Step 4: Build a Basic Streaming UI

The next thing we’re going to do is to set up the UI layout, including adding a broadcast button that will enable a user to capture and publish video from their mobile device to a stream running on Wowza Streaming Cloud or Wowza Streaming Engine.

To add a broadcast button to the app interface:

  1. In the Project navigator, select Main.storyboard and then the View Controller.
  2. Drag a button from the Object library onto the Interface Builder canvas.
  3. Select the Button text and rename it Broadcast.
  4. Add a referencing outlet called broadcastButton to ViewController.m.
@interface ViewController ()

// Referencing outlet for the broadcast button
@property (weak, nonatomic) IBOutlet UIButton *broadcastButton;

@end

If you did not do so when installing GoCoder SDK, add an #import statement to the GoCoder SDK’s API header, and then add the goCoder property to the ViewController class defined in ViewController.m.

#import <WowzaGoCoderSDK/WowzaGoCoderSDK.h>

@interface ViewController ()

// The top level GoCoder API interface
@property (nonatomic, strong) WowzaGoCoder *goCoder;

// Referencing outlet for the broadcast button
@property (weak, nonatomic) IBOutlet UIButton *broadcastButton;

@end

Next, register your GoCoder SDK license and initialize the SDK by adding the following to the viewDidLoad method of the ViewController class in ViewController.m (replace GOSK-XXXX-XXXX-XXXX-XXXX-XXXX with your registered license key).

// Register the GoCoder SDK license key
NSError *goCoderLicensingError = [WowzaGoCoder registerLicenseKey:@"GOSK-XXXX-XXXX-XXXX-XXXX-XXXX"];
if (goCoderLicensingError != nil) {
    // Log license key registration failure
    NSLog(@"%@", [goCoderLicensingError localizedDescription]);
} else {
    // Initialize the GoCoder SDK
    self.goCoder = [WowzaGoCoder sharedInstance];
}

Defining Application Permissions (iOS 10 and newer)

For iOS 10 and later, you must define the messages that are displayed to users when the app requests permissions to access the camera and microphone.

  1. Select your build target in the Project Inspector and click the Info tab.
  2. Add the following Custom iOS Target Properties:
    • Add a string-valued key named Privacy - Camera Usage Description with the value The camera will be used to capture video for live streaming.
    • Add a string-valued key named Privacy - Microphone Usage Description with the value The microphone will be used to capture audio for live streaming.

Setting Up the Camera Preview

Next we will be connecting the camera preview functionality within the the SDK with the UI we have just created. This is done by adding the viewDidLoad method of the ViewController class in ViewController.m.

if (self.goCoder != nil) {
   // Associate the U/I view with the SDK camera preview
   self.goCoder.cameraView = self.view;

   // Start the camera preview
   [self.goCoder.cameraPreview startPreview];
}

Configure Broadcasting to a Stream

The mobile application we are building will need to connect to a live stream running in Wowza Streaming Cloud or Wowza Streaming Engine for processing and delivery to viewers. In this step, we are adding the necessary functionality to connect streams. Module 2: Processing includes detail on how this information is obtained from within the API response for creating a stream.

Add the hostAddress, portNumber, applicationName and streamName for your Wowza Streaming Engine server or Wowza Streaming Cloud account to the viewDidLoad method of the ViewController class in ViewController.m.

// Get a copy of the active config
WowzaConfig *goCoderBroadcastConfig = self.goCoder.config;

// Set the defaults for 720p video
[goCoderBroadcastConfig loadPreset:WZFrameSizePreset1280x720];

// Set the connection properties for the target 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;

Add Broad Status Callbacks

The next thing we will be doing is adding the capability for the application to provide the user with a notification of a successful connection to a running live stream, as well as to display any errors reported to the mobile app.

Add Broad Status Callbacks

The next thing we will be doing is adding the capability for the application to provide the user with a notification of a successful connection to a running live stream, as well as to display any errors reported to the mobile app.

  • Update the interface definition for the ViewController class to include theWZStatusCallback protocol, which monitors status updates and errors during the live stream broadcast.
// Implements the WZStatusCallback protocol
@interface ViewController () <WZStatusCallback>
  • Add the methods defined by theWZStatusCallback protocol to the ViewController class to monitor status updates and errors during a live-streaming broadcast.
- (void) onWZStatus:(WZStatus *) goCoderStatus {
    // A successful status transition has been reported by the GoCoder SDK
    NSString *statusMessage = nil;

    switch (goCoderStatus.state) {
        case WZStateIdle:
            statusMessage = @"The broadcast is stopped";
            break;

        case WZStateStarting:
            statusMessage = @"Broadcast initialization";
            break;

        case WZStateRunning:
            statusMessage = @"Streaming is active";
            break;

        case WZStateStopping:
            statusMessage = @"Broadcast shutting down";
            break;
    }

    if (statusMessage != nil)
        NSLog(@"Broadcast status: %@", statusMessage);
}

- (void) onWZError:(WZStatus *) goCoderStatus {
    // If an error is reported by the GoCoder SDK, display an alert dialog
    // containing the error details using the U/I thread
    dispatch_async(dispatch_get_main_queue(), ^{
        UIAlertView *alertDialog =
            [[UIAlertView alloc] initWithTitle:@"Streaming Error"
                                  message:goCoderStatus.description
                                 delegate:nil
                        cancelButtonTitle:@"OK"
                        otherButtonTitles:nil];
        [alertDialog show];
    });
}

Configure the Start/Stop Streaming Button

The next step is to configure the button that will start and stop broadcasting to the live stream.

  • Add an event handler method to the ViewController class to be invoked when the broadcast button is pressed.
- (IBAction)broadcastButtonTapped:(UIButton *)button
{
    // Ensure the minimum set of configuration settings have been specified necessary to
    // initiate a broadcast streaming session
    NSError *configValidationError = [self.goCoder.config validateForBroadcast];

    if (configValidationError != nil) {
        UIAlertView *alertDialog =
            [[UIAlertView alloc] initWithTitle:@"Incomplete Streaming Settings"
                        message: self.goCoder.status.description
                        delegate:nil
                        cancelButtonTitle:@"OK"
                        otherButtonTitles:nil];
        [alertDialog show];
    } else if (self.goCoder.status.state != WZStateRunning) {
        // Start streaming
        [self.goCoder startStreaming:self];
    }
    else {
        // Stop the broadcast that is currently running
        [self.goCoder endStreaming:self];
    }    
}

  • Associate the broadcastButtonTapped event handler with the broadcast button by adding the following to the bottom of the viewDidLoad method.
[self.broadcastButton addTarget:self action:@selector(broadcastButtonTapped:)
    forControlEvents:UIControlEventTouchUpInside];

Build and Deploy your iOS Live Streaming Broadcast App

At this point we are ready to build and deploy the app. From the Product menu select Run to finish building your iOS app, capable of live streaming to Wowza Streaming Cloud or Wowza Streaming Engine.

The completed code for the ViewController class for a completed project in this module should appear as follows:

#import "ViewController.h"
#import <WowzaGoCoderSDK/WowzaGoCoderSDK.h>

@interface ViewController () <WZStatusCallback>

// The top level GoCoder API interface
@property (nonatomic, strong) WowzaGoCoder *goCoder;

// Referencing outlet for the broadcast button
@property (weak, nonatomic) IBOutlet UIButton *broadcastButton;

@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.

  // Register the GoCoder SDK license key
  NSError *goCoderLicensingError = [WowzaGoCoder registerLicenseKey:@"GOSK-XXXX-XXXX-XXXX-XXXX-XXXX"];
  if (goCoderLicensingError != nil) {
    // Log license key registration failure
    NSLog(@"%@", [goCoderLicensingError localizedDescription]);
  } else {
    // Initialize the GoCoder SDK
    self.goCoder = [WowzaGoCoder sharedInstance];
  }

  if (self.goCoder != nil) {
    // Associate the U/I view with the SDK camera preview
    self.goCoder.cameraView = self.view;

    // Start the camera preview
    [self.goCoder.cameraPreview startPreview];
  }

  // Get a copy of the active config
  WowzaConfig *goCoderBroadcastConfig = self.goCoder.config;

  // Set the defaults for 720p video
  [goCoderBroadcastConfig loadPreset:WZFrameSizePreset1280x720];

  // Set the connection properties for the target 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;

  [self.broadcastButton addTarget:self action:@selector(broadcastButtonTapped:)
           forControlEvents:UIControlEventTouchUpInside];
}

- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
  // Dispose of any resources that can be recreated.
}

- (void) onWZStatus:(WZStatus *) goCoderStatus {
  // A successful status transition has been reported by the GoCoder SDK
  NSString *statusMessage = nil;

  switch (goCoderStatus.state) {
    case WZStateIdle:
      statusMessage = @"The broadcast is stopped";
      break;

    case WZStateStarting:
      statusMessage = @"Broadcast initialization";
      break;

    case WZStateRunning:
      statusMessage = @"Streaming is active";
      break;

    case WZStateStopping:
      statusMessage = @"Broadcast shutting down";
      break;
  }

  if (statusMessage != nil)
    NSLog(@"Broadcast status: %@", statusMessage);
}

- (void) onWZError:(WZStatus *) goCoderStatus {
  // If an error is reported by the GoCoder SDK, display an alert dialog
  // containing the error details using the U/I thread
  dispatch_async(dispatch_get_main_queue(), ^{
    UIAlertView *alertDialog =
    [[UIAlertView alloc] initWithTitle:@"Streaming Error"
                   message:goCoderStatus.description
                  delegate:nil
             cancelButtonTitle:@"OK"
             otherButtonTitles:nil];
    [alertDialog show];
  });
}

- (IBAction)broadcastButtonTapped:(UIButton *)button
{
  // Ensure the minimum set of configuration settings have been specified necessary to
  // initiate a broadcast streaming session
  NSError *configValidationError = [self.goCoder.config validateForBroadcast];

  if (configValidationError != nil) {
    UIAlertView *alertDialog =
    [[UIAlertView alloc] initWithTitle:@"Incomplete Streaming Settings"
                   message: self.goCoder.status.description
                  delegate:nil
             cancelButtonTitle:@"OK"
             otherButtonTitles:nil];
    [alertDialog show];
  } else if (self.goCoder.status.state != WZStateRunning) {
    // Start streaming
    [self.goCoder startStreaming:self];
  }
  else {
    // Stop the broadcast that is currently running
    [self.goCoder endStreaming:self];
  }    
}

@end

Android: Building a Simple Android Mobile Streaming Application

In this section, we will cover the steps necessary to build a simple Android mobile streaming application. Using Android Studio and the Wowza GoCoder SDK, this app will enable Android mobile devices to:

  • Capture streaming video and audio content.
  • Encode the content.
  • Publish the content to a live-streaming workflow .

Note: If you wish to only build an iOS version and skip building an Android app, go to the next section: Building a Simple iOS Mobile Streaming Application.

If you prefer not to build your own mobile streaming application at all, the Wowza branded version of the GoCoder application is available free of charge on the AppStore and GooglePlay, for use with Wowza Streaming Cloud. In this case, you can skip ahead to Module 2: Processing.

Before you begin you will need:

  • An Android device with OS version 4.4 or newer.
  • An active Wowza Streaming Cloud plan.
  • An active license of the Wowza GoCoder SDK (with an Android package name).
  • Android Studio 1.2.0 or newer.

Creating an Android App Project and Installing and Configuring GoCoder SDK

In this step, we are creating an application project to attach and build upon functionality within the GoCoder SDK that will turn an Android mobile device into a streaming camera and encoder.

Creating an Android Studio App Project

Begin by creating an app project in Android Studio. After launching/starting Android Studio:

  1. In the Welcome to Android Studio window, click Start a New Android Studio Project.
  2. On the New Project page of the Create New Project window, click Edit to edit the Package Name, and enter the application package name that was used to generate your SDK license key, and then click Next.
  3. On the Target Android Devices page, select the form factors your app will run on (typically Phone and Tablet), select a minimum SDK version of API 19 or higher, and then click Next.
  4. On the Add an Activity to Mobile page, select Empty Activity and then click Next.
  5. On the Customize the Activity page, accept the default values and click Finish.

Installing/Adding the Android GoCoder SDK to your project

Now that a project has been created, the next step is to mount the GoCoder SDK within the project.

  1. Download the current release ofWowza GoCoder SDK for Android.
  2. Expand the downloaded .zip file, which contains the following files and folders:

com.wowza.gocoder.sdk.aar

The Android archive file that contains the GoCoder SDK library.

/docs

The SDK API reference documentation in HTML format. Open index.html in this folder to view the documentation locally.

/ModuleGoCoderSDKSample

Sample code that demonstrates integration between Wowza Streaming Engine media server software and the SDK.

Release_Notes.md

A markdown file that containsthese release notes.

/sample-app

A sample application that can be imported into Android Studio that demonstrates many of the SDK’s capabilities.

  • In the file system, copy com.wowza.gocoder.sdk.aar from the expanded .zip file to the project’s libs folder. By default, the libs folder is located at [project_root]/app/libs.
  • In Android Studio, open build.gradle (Module:app), which you can find in the Gradle Scripts section of the tool window.
  • In the editor window, add a dependency for the SDK library by adding the following code into the file as described:
// This enables Gradle to locate Android library dependencies in the same manner as jar-based dependencies. Add to the bottom of the file.

repositories {
    flatDir {
        dirs 'libs'
    }
}

// Copy these two compile dependencies into the existing dependencies section of the file.

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    // The GoCoder SDK library dependency declaration

    compile 'com.wowza.gocoder.sdk.android:com.wowza.gocoder.sdk:1.0b7@aar'

    // List additional dependencies here...

}
  • In the tool window, navigate to app/manifests and open the AndroidManifest.xml file.
  • Add the following permissions below the package declaration, above the application element:
<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" />

At this point, you can begin building onto the SDK in your application project.

Build a Basic Streaming UI

The next thing we are going to do is to set up the UI layout, including adding a broadcast button that will enable a user to capture and publish video from their mobile device to a stream running on Wowza Streaming Cloud or Wowza Streaming Engine.

We will begin by adding a broadcast button to the app’s layout definition file.

  • In Android Studio, click File and then click Open.
  • Navigate to and open [project_root]/app/src/main/res/layout/activity_main.xml.
  • Replace the contents of the activity_main.xml file with the following, making sure to replace com.mycompany.myapp in the tools:context property with the package name for the app’s 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>
  • Add the android:configChanges property to the definition of the activity element in the [project_root]/app/src/main/AndroidManifest.xml file.
<activity android:name=".MainActivity"
    android:configChanges="orientation|keyboardHidden|screenSize">
//
// 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);
}

Defining the Android Live Streaming App Main Properties

Now that we have established the basic UI, we will enable the main properties.

Add the following member variables to the MainActivity class in MainActivity.java.

public class MainActivity extends AppCompatActivity {

    // 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 int PERMISSIONS_REQUEST_CODE = 0x1;
    private boolean mPermissionsGranted = true;
    private String[] mRequiredPermissions = new String[] {
            Manifest.permission.CAMERA,
            Manifest.permission.RECORD_AUDIO
    };

Register and Initialize the Mobile Live Streaming SDK

Next, you must register your GoCoder SDK license and initialize the SDK by adding the following to the onCreate() method in the MainActivity class. (replace GOSK-XXXX-XXXX-XXXX-XXXX-XXXX with your license key)

// 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;
}

Define App Permissions

Next, we must define the permissions to access all of the OS and device functions the app requires to function properly.

  • Add the following permissions to the [project_root]/app/src/main/AndroidManifest.xml file just below the top-level element.
<?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" />
  • For Android 6.0 and later, add the onResume() and onRequestPermissionsResult() methods to the MainActivity class to check that the user has granted the app permission to access the camera and microphone.
//
// 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;
}

Setting Up the Camera Preview

Now we will be configuring and starting the camera preview by connecting the video and audio control functionality to the preview in our UI.

  • Associate the view defined in the activity’s layout with the correspondingWZCameraView class member, and create an audio input device instance by adding the following to the onCreate() method in the MainActivity class just below the SDK initialization:
// 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();
  • Add the following to the bottom of the onResume() method in the MainActivity class (after the permission checking logic) to turn on the camera preview when the app is brought to the foreground.
// Start the camera preview display
if (mPermissionsGranted && goCoderCameraView != null) {
    if (goCoderCameraView.isPreviewPaused())
        goCoderCameraView.onResume();
    else
        goCoderCameraView.startPreview();    
}

Configure Broadcasting to a Stream

The mobile application we are building will need to connect to a live stream running in Wowza Streaming Cloud or in Wowza Streaming Engine for processing and delivery to viewers. In this step we are adding the necessary functionality to connect streams. Module 2: Processing includes detail on how this information is obtained from within the API response for creating a stream.

Add the hostAddress, portNumber, applicationName and streamName for your Wowza Streaming Engine server or Wowza Streaming Cloud account to the onCreate() method in the MainActivity class.

// 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 Engine server 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 source
goCoderBroadcastConfig.setVideoBroadcaster(goCoderCameraView);

// Designate the audio device as the audio broadcaster
goCoderBroadcastConfig.setAudioBroadcaster(goCoderAudioDevice);

Add Broad Status Callbacks

The next thing we will be doing is adding the capability for the application to provide the user with a notification of successful connection to a running live stream, as well as to display any errors reported to the mobile app.

Add theWZStatusCallback interface to the primary activity class (MainActivity) definition to monitor status updates and errors during a live-streaming broadcast.

// Main app activity class
public class MainActivity extends AppCompatActivity
    implements WZStatusCallback {

Add the methods defined by theWZStatusCallback interface to the MainActivity class to monitor status updates and errors during a live-streaming broadcast.

//
// The callback invoked upon changes to the state of the steaming broadcast
//
@Override
public void onWZStatus(final WZStatus goCoderStatus) {
 // A successful status transition has been reported by the GoCoder SDK
 final StringBuffer statusMessage = new StringBuffer("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/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();
  }
 });
}

Configure the Start/Stop Streaming Button

The next step is to configure the button that will start and stop broadcasting to the live stream.

  • Add the View.onClickListener interface to the MainActivity class definition.
// Main app activity class
public class MainActivity extends AppCompatActivity
  implements  WZStatusCallback, View.OnClickListener {
  • Add an onClick() event handler to the bottom of the onCreate() method in the MainActivity class to be invoked when the broadcast button is pressed.
//
// 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() event handler with the broadcast button defined in the activity’s layout by adding the following to the bottom of the onCreate() method in the MainActivity class:
// 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);

Build and Deploy Your Android Live Streaming Broadcast App

At this point we are ready to build and deploy the app. Click the Run menu, choose Run or Debug and then select a device for deployment. This will finish building your Android app capable of live streaming to Wowza Streaming Cloud or Wowza Streaming Engine.

The completed code for the ViewController class for completed project in this module should appear as follows:

public class MainActivity extends AppCompatActivity
  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 the corresponding 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 Engine server 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 broadcast button'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 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;

  // Start the camera preview display
  if (mPermissionsGranted && goCoderCameraView != null) {
    if (goCoderCameraView.isPreviewPaused())
    goCoderCameraView.onResume();
    else
    goCoderCameraView.startPreview();
  }

  }

  //
  // 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;
  }

  //
  // 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);
  }
  }

  //
  // The callback invoked upon changes to the state of the steaming broadcast
  //
  @Override
  public void onWZStatus(final WZStatus goCoderStatus) {
  // A successful status transition has been reported by the GoCoder SDK
  final StringBuffer statusMessage = new StringBuffer("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/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);
  }

}