1

我可以通過Youtube API創建一個活動。活動正在顯示在我的Youtube頁面「活動標籤」(即將到來的類別)中。 但是,當我開始流式傳輸時,它會返回錯誤403(流處於非活動狀態)。我希望每次開始播放時創建一個新的廣播事件。 任何幫助將不勝感激。這裏是我的代碼....通過Android在YouTube上直播時的非活動流

這是AsyncTask

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

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

在方法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(); 
    } 
} 

這裏是我的代碼開始的事件流:

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

我收到的異常日誌:

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 

回答

1

我幾天前面臨類似的問題......這就是我所做的。 ..

要記住的要點:a)您可能會在API中接收到比youtube事件門戶更差的RTMP網址。

b)無論您是否能夠「上線」,您都可能在將廣播轉換爲狀態「Live」時收到403錯誤。

現在的問題與錯誤403(流無效):

確保呼籲「在廣播過渡」之前你發送幀/數據到YouTube。 現在在差異線程上調用「Broadcast.transition」,並使用線程暫停幾秒(可能需要一些時間才能將數據發送到YouTube)。

如:

公共靜態無效startEvent(YouTube最終的YouTube,最後絃樂廣播ID) 拋出IOException異常{

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

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

我希望這有助於....

1

您需要等待流狀態切換到active。所以你必須存儲你的流ID和你的廣播ID等待這個狀態。

的檢查可以使用TimerTaskScheduledExecutorService來完成:

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

checkStreamStatus方法檢查流狀態,也可以檢查數據流的健康,如果你需要它(健康狀況"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(); 
     } 
    } 
} 

下面是一個完整的例子:

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

啓動這個例子與new YoutubeTask().execute();

您可以直接從oauth playground與範圍https://www.googleapis.com/auth/youtube採取一個訪問令牌進行測試。此外,爲了測試流,我建議使用Android應用RTMP Camera測試相機流到Youtube,您可以直接將流名稱XXXX-XXXX-XXXX-XXXX