Reputation: 343
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
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
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