Recording a Live Stream

Once the LiveContribution has been created, you are finally ready to stream.

Create the LiveRecorder

The audio and video live streaming is performed by a LiveRecorder object in association with a LiveRecorderView interface to implement.

The LiveRecorder needs to be instantiated only once, so you need to use it as a singleton while the view will follow the Android life cycle.

Here's how to create the LiveRecorder:

LiveRecorder liveRecorder = new LiveRecorder(liveContribution);

Activity life cycle

Since you'll use an Activity or a Fragment as a LiveRecorderView, you may want to avoid your view to be re-created while streaming. We recommand that the activity used as LiveRecorderView supports only 1 orientation and avoid changing it during streaming.

In any case, you should ALWAYS use the LiveRecorder as a singleton and avoid re-creating it if your Activity is destroyed and re-created.

Implement the LiveRecorderView

To be able to use the LiveRecorder you need to implement the LiveRecorderView.

We recommand using an AspectFrameLayout (available in the Live SDK) as a container for the camera preview since it will automaticaly adopt the right aspect depending on the camera.

Here's a basic sample of a layout (activity_live.xml) with the camera preview container for an activity:

<?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:background="@android:color/black">

    <com.newzulu.livesdk.AspectFrameLayout
        android:id="@+id/live_preview_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

And here's how the implementation is done:

public class LiveActivity extends AppCompatActivity implements LiveRecorderView
{
    private AspectFrameLayout mPreviewContainer;

    // This singleton implementation is up to you
    @NonNull
    private final LiveRecorder mLiveRecorder = getLiveRecorderInstance();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_live);

        mPreviewContainer = (AspectFrameLayout) findViewById(R.id.live_preview_container);
    }

    @Override
    protected void onStart()
    {
        super.onStart();

        mLiveRecorder.onStart(this);
    }

    @Override
    protected void onResume()
    {
        super.onResume();

        mLiveRecorder.onResume(this);
    }

    @Override
    protected void onPause()
    {
        mLiveRecorder.onPause(this);

        super.onPause();
    }

    @Override
    protected void onStop()
    {
        mLiveRecorder.onStop(this);

        super.onStop();
    }

    @NonNull
    @Override
    public Context getContext()
    {
        return this;
    }

    @NonNull
    @Override
    public ViewGroup getPreviewContainer()
    {
        return mPreviewContainer;
    }

    @Override
    public boolean isViewChangingConfigurations()
    {
        return isChangingConfigurations();
    }

    @Override
    public boolean hasPermission(@NonNull String permission)
    {
        return ContextCompat.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
    }

    @Override
    public void onPreviewRatioChanged(double ratio)
    {
        mPreviewContainer.setAspectRatio(ratio);
    }

    @Override
    public void onLiveRecorderError(final LiveException e)
    {
        // An error occurred with the live recorder.
    }

    @Override
    public void onRecordingStatusChange(boolean isStarted)
    {
        // The recording has started or stopped.
    }
}

Recording

When the user taps the record button, you invoke the startRecording() method and when the user taps the record button again, you invoke the stopRecording() method.

Here's a way to do it:

mRecordButton = (Button) findViewById(R.id.record_button);

mRecordButton.setOnClickListener(new View.OnClickListener()
{
    @Override
    public void onClick(View view)
    {
        if (mLiveRecorder != null)
        {
            if (mLiveRecorder.isRecording())
            {
                mLiveRecorder.stopRecording();
            }
        else
            {
                mLiveRecorder.startRecording();
            }
        }
    }
});

Note the use of the isRecording() method to check if the LiveRecorder is not already recording a stream.

Stopping the recording could takes some time as the recorder needs to send all pending audio and video to the Newzulu Live streaming server before completing. You should wait until receiving a callback indicating the record has stopped.

You may stop and restart the recording as many times as you want.

Responding to events

In a normal workflow the actions are asynchronous and errors may arise, so callbacks are provided by the LiveRecorderView implementation.

Note: those callbacks are called from a worker thread so if you perform an action on a UI object an error will arise from the linter.

Here's an example for an activity:

@Override
public void onRecordingStatusChange(boolean isStarted)
{
    if (isStarted)
    {
        runOnUiThread(new Runnable()
        {
            @Override
            public void run()
            {
                mRecordButton.setText("Stop recording");
            }
        });
    }
    else
    {
        runOnUiThread(new Runnable()
        {
            @Override
            public void run()
            {
                mRecordButton.setText("Start recording");
            }
        });
    }
}

Handling errors

An error may arise and unexpectedly interrupt the recording, they will rise via onLiveRecorderError(LiveException e), a cause can help for troubleshooting but it usualy means a blocking error has been encountered.

@Override
public void onLiveRecorderError(final LiveException e)
{
    // An error occured with the live recorder.
}

The error has a Type property that can be one of the following cases:

  • NETWORK: A network error occurred and the SDK was unable to reach the remote server.
  • SERVICE_ERROR: The remote server encountered an error, you should try again later.
  • MISSING_PERMISSION: A mandatory permission is missing.
  • HARDWARE: An hardware error occurred using the camera or the microphone.
  • UNKNOWN: An unknown internal error occurred.

Extras

The LiveRecorder comes with a few helper methods to manage the camera:

  • setTorchOn(boolean torchOn) - to start/stop the torch if available on the device with the selected camera.
  • toggleCamera() - to toggle back/front camera if available.
  • focus() - to force manual focus (should not be necessary).

Finish your contribution

Once you're done recording, you need to finish the Live Contribution.

Next step: Finish your contribution.