Sachin
Sachin

Reputation: 343

Inactive stream when going live on youtube through android

I am able to create an event through Youtube api. Event is getting displayed in my Youtube page "events tab" (upcoming category). But when I start streaming it returns the error 403 (stream is inactive).I want to create a new broadcast event everytime I am going live. Any help will be appreciated. Here is my code....

This is in AsyncTask :

YouTube youtube = new YouTube.Builder(transport, jsonFactory,
        credential).setApplicationName(APP_NAME).build();

YouTubeApi.createLiveEvent(youtube, "event description", "event name");    

In method YoutubeApi.createLiveEvent(...) :

public static void createLiveEvent(YouTube youtube, String description,
    String name) {

    try {

        LiveBroadcastSnippet broadcastSnippet = new LiveBroadcastSnippet();
        broadcastSnippet.setTitle(name);
        broadcastSnippet.setScheduledStartTime(new DateTime(new Date()));

        LiveBroadcastContentDetails contentDetails = new LiveBroadcastContentDetails();
        MonitorStreamInfo monitorStream = new MonitorStreamInfo();
        monitorStream.setEnableMonitorStream(false);
        contentDetails.setMonitorStream(monitorStream);

        // Create LiveBroadcastStatus with privacy status.
        LiveBroadcastStatus status = new LiveBroadcastStatus();
        status.setPrivacyStatus("public");


        LiveBroadcast broadcast = new LiveBroadcast();
        broadcast.setKind("youtube#liveBroadcast");
        broadcast.setSnippet(broadcastSnippet);
        broadcast.setStatus(status);
        broadcast.setContentDetails(contentDetails);

        // Create the insert request
        YouTube.LiveBroadcasts.Insert liveBroadcastInsert = youtube
            .liveBroadcasts().insert("snippet,status,contentDetails",
                broadcast);

        // Request is executed and inserted broadcast is returned
        LiveBroadcast returnedBroadcast = liveBroadcastInsert.execute();

        // Create a snippet with title.
        LiveStreamSnippet streamSnippet = new LiveStreamSnippet();
        streamSnippet.setTitle(name);

        // Create content distribution network with format and ingestion
        // type.
        CdnSettings cdn = new CdnSettings();
        cdn.setFormat("240p");
        cdn.setIngestionType("rtmp");

        LiveStream stream = new LiveStream();
        stream.setKind("youtube#liveStream");
        stream.setSnippet(streamSnippet);
        stream.setCdn(cdn);

        // Create the insert request
        YouTube.LiveStreams.Insert liveStreamInsert = youtube.liveStreams()
            .insert("snippet,cdn", stream);

        // Request is executed and inserted stream is returned
        LiveStream returnedStream = liveStreamInsert.execute();

        // Create the bind request
        YouTube.LiveBroadcasts.Bind liveBroadcastBind = youtube
            .liveBroadcasts().bind(returnedBroadcast.getId(),
                "id,contentDetails");

        // Set stream id to bind
        liveBroadcastBind.setStreamId(returnedStream.getId());

        // Request is executed and bound broadcast is returned
        liveBroadcastBind.execute();

    } catch (GoogleJsonResponseException e) {
        System.err.println("GoogleJsonResponseException code: " + e.getDetails().getCode() + " : " + e.getDetails().getMessage());
        e.printStackTrace();

    } catch (IOException e) {
        System.err.println("IOException: " + e.getMessage());
        e.printStackTrace();
    } catch (Throwable t) {
        System.err.println("Throwable: " + t.getStackTrace());
        t.printStackTrace();
    }
}

Here is my code to start event stream :

public static void startEvent(YouTube youtube, String broadcastId) // broadcast id is same(checked)
        throws IOException {

    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        Log.e(APP_NAME, "", e);
    }

    YouTube.LiveStreams.List liveBroadcastRequest = youtube
            .liveStreams().list("id,snippet,status");
    // liveBroadcastRequest.setMine(true);
    liveBroadcastRequest.setId(broadcastId);  //  setBroadcastStatus("upcoming");



    // List request is executed and list of broadcasts are returned
    LiveStreamListResponse returnedListResponse = liveBroadcastRequest.execute();
    List<LiveStream> returnedList = returnedListResponse.getItems();


    Transition transitionRequest = youtube.liveBroadcasts().transition(
            "live", broadcastId, "status");
    transitionRequest.execute();
}

The logs of the exception I am receiving:

com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden
{
  "code": 403,
  "errors": [
    {
      "domain": "youtube.liveBroadcast",
      "message": "Stream is inactive",
      "reason": "errorStreamInactive",
      "extendedHelp": "https://developers.google.com/youtube/v3/live/docs/liveBroadcasts/transition"
    }
  ],
  "message": "Stream is inactive"
}
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:321)
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1065)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
at net.ossrs.yasea.demo.utils.YouTubeApi.startEvent(YouTubeApi.java:236)
at net.ossrs.yasea.demo.rtmp.YoutubeRTMPUrl$StartEventTask.doInBackground(YoutubeRTMPUrl.java:160)
at net.ossrs.yasea.demo.rtmp.YoutubeRTMPUrl$StartEventTask.doInBackground(YoutubeRTMPUrl.java:144)
at android.os.AsyncTask$2.call(AsyncTask.java:295)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818

Upvotes: 1

Views: 2150

Answers (2)

Mohit Aggarwal
Mohit Aggarwal

Reputation: 36

I was facing similar issue few days back...here is what i did...

Points to remember: a) you may receive a diff RTMP url in API than the youtube events portal.

b) you may receive 403 error while calling transition on broadcast to status "Live" irrespective of whether you are able to "go live" or not.

Now issue with error 403(stream is inactive):

Make sure you are sending frames/data to youtube before calling "transition on broadcast". Now call "Broadcast.transition" on a diff thread with a thread-pause of few seconds(It may take time to send data to youtube).

eg:

public static void startEvent(final YouTube youtube, final String broadcastId) throws IOException {

    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        Log.e(APP_NAME, "", e);
    }

    Transition transitionRequest = youtube.liveBroadcasts().transition(
            "live", broadcastId, "status");
    transitionRequest.execute();
}

I hope this helps....

Upvotes: 2

Bertrand Martel
Bertrand Martel

Reputation: 45352

You need to wait for the stream status to switch to active. So you have to store your stream ID and your broadcast ID waiting for this status.

The check can be done using a TimerTask or a ScheduledExecutorService :

mScheduleTaskExecutor = Executors.newSingleThreadScheduledExecutor();

// check every 2 seconds the stream status
mScheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        try {
            if (mStreamId != null) {
                checkStreamStatus();
            }
        } catch (IOException e) {
            Log.e(TAG, null, e);
        }
    }
}, 0, 2, TimeUnit.SECONDS);

The checkStreamStatus method check the stream status, it could also check the stream health if you need it (health status"GOOD") :

public void checkStreamStatus() throws IOException {

    YouTube.LiveStreams.List livestreamRequest = youtube.liveStreams().list("status");
    livestreamRequest.setId(mStreamId);

    LiveStreamListResponse returnedListResponse = livestreamRequest.execute();
    List < LiveStream > returnedList = returnedListResponse.getItems();

    if (returnedList.size() == 1) {

        LiveStream stream = returnedList.get(0);

        Log.v(TAG, "the current stream status is : " + stream.getStatus().getStreamStatus());

        if (stream.getStatus().getStreamStatus().equals("active")) {
            Log.v(TAG, "start broadcasting now");
            startEvent();
            mScheduleTaskExecutor.shutdownNow();
        }
    }
}

Here is a complete example :

public class YoutubeTask extends AsyncTask < Void, Void, String > {

    private final static String TAG = YoutubeTask.class.getSimpleName();

    private ScheduledExecutorService mScheduleTaskExecutor;

    private String mStreamId;
    private String mBroadcastId;

    private static YouTube youtube;

    @Override
    protected String doInBackground(Void...params) {

        mScheduleTaskExecutor = Executors.newSingleThreadScheduledExecutor();

        // Authorize the request.
        Credential credential = new GoogleCredential().setAccessToken("...");

        // This object is used to make YouTube Data API requests.
        youtube = new YouTube.Builder(new NetHttpTransport(), new JacksonFactory(), credential)
            .setApplicationName("youtube-cmdline-createbroadcast-sample").build();

        createLiveEvent(youtube, "event name");

        // check every 2 seconds the stream status
        mScheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    if (mStreamId != null) {
                        checkStreamStatus();
                    }
                } catch (IOException e) {
                    Log.e(TAG, null, e);
                }
            }
        }, 0, 2, TimeUnit.SECONDS);

        return null;
    }

    public void createLiveEvent(YouTube youtube, String name) {
        try {
            LiveBroadcastSnippet broadcastSnippet = new LiveBroadcastSnippet();
            broadcastSnippet.setTitle(name);
            broadcastSnippet.setScheduledStartTime(new DateTime(new Date()));

            LiveBroadcastContentDetails contentDetails = new LiveBroadcastContentDetails();
            MonitorStreamInfo monitorStream = new MonitorStreamInfo();
            monitorStream.setEnableMonitorStream(false);
            contentDetails.setMonitorStream(monitorStream);

            LiveBroadcastStatus status = new LiveBroadcastStatus();
            status.setPrivacyStatus("public");

            LiveBroadcast broadcast = new LiveBroadcast();
            broadcast.setKind("youtube#liveBroadcast");
            broadcast.setSnippet(broadcastSnippet);
            broadcast.setStatus(status);
            broadcast.setContentDetails(contentDetails);

            YouTube.LiveBroadcasts.Insert liveBroadcastInsert = youtube
                .liveBroadcasts().insert("snippet,status,contentDetails",
                    broadcast);

            LiveBroadcast returnedBroadcast = liveBroadcastInsert.execute();

            LiveStreamSnippet streamSnippet = new LiveStreamSnippet();
            streamSnippet.setTitle(name);

            CdnSettings cdn = new CdnSettings();
            cdn.setFormat("240p");
            cdn.setIngestionType("rtmp");

            LiveStream stream = new LiveStream();
            stream.setKind("youtube#liveStream");
            stream.setSnippet(streamSnippet);
            stream.setCdn(cdn);

            // Create the insert request
            YouTube.LiveStreams.Insert liveStreamInsert = youtube.liveStreams().insert("snippet,cdn", stream);

            // Request is executed and inserted stream is returned
            LiveStream returnedStream = liveStreamInsert.execute();

            // Create the bind request
            YouTube.LiveBroadcasts.Bind liveBroadcastBind = youtube.liveBroadcasts().bind(returnedBroadcast.getId(),
                "id,contentDetails");

            // Set stream id to bind
            liveBroadcastBind.setStreamId(returnedStream.getId());

            // Request is executed and bound broadcast is returned
            liveBroadcastBind.execute();

            // store stream Id & broadcast Id
            mStreamId = returnedStream.getId();
            mBroadcastId = returnedBroadcast.getId();

        } catch (GoogleJsonResponseException e) {
            Log.e(TAG, null, e);
        } catch (IOException e) {
            Log.e(TAG, null, e);
        } catch (Throwable t) {
            Log.e(TAG, null, t);
        }
    }

    public void checkStreamStatus() throws IOException {

        YouTube.LiveStreams.List livestreamRequest = youtube.liveStreams().list("status");
        livestreamRequest.setId(mStreamId);

        LiveStreamListResponse returnedListResponse = livestreamRequest.execute();
        List < LiveStream > returnedList = returnedListResponse.getItems();

        if (returnedList.size() == 1) {

            LiveStream stream = returnedList.get(0);

            Log.v(TAG, "the current stream status is : " + stream.getStatus().getStreamStatus());

            if (stream.getStatus().getStreamStatus().equals("active")) {
                Log.v(TAG, "start broadcasting now");
                startEvent();
                mScheduleTaskExecutor.shutdownNow();
            }
        }
    }

    public void startEvent() throws IOException {
        YouTube.LiveBroadcasts.Transition transitionRequest = youtube.liveBroadcasts().transition(
            "live", mBroadcastId, "status");
        transitionRequest.execute();
    }
}

Launch this example with new YoutubeTask().execute();

You can test this directly with an access token taken from oauth playground with scope https://www.googleapis.com/auth/youtube. Also for testing the stream, I recommend to use the Android app RTMP Camera for testing camera stream to Youtube where you can directly put the Stream Name XXXX-XXXX-XXXX-XXXX

Upvotes: 1

Related Questions