2014年10月7日 星期二

如何將live stream發佈至YouTube(using YouTube Live Streaming API)

準備工作:
1.若沒有Google Account,請先申請一個
2.申請完成,請至Google Developers Console
3.(Optional)建議可先至帳戶設定將格式改為 台灣,當然如果你在台灣以外的國家請自行選擇
4.在專案選項內,選 "建立專案",名稱請自取,我這邊取名為StreamingTest
5.至"API和驗證"下選擇API,先將YouTube Data API v3 啟用
6.選擇"憑證","建立新的用戶端ID",在新對話方塊選擇"已安裝的應用程式"(桌上型應用程式),完成後會在畫面上看到 用戶端ID(client ID及用戶端密碼(client secret),這兩個在之後的授權驗證會需要用到,要開始寫程式前的前置工作大致完成
7.在開始用程式建立一個可將自己的Live Stream發佈至Youtube前,也可以先至現場直播中心,完整的建立一次現場直播流程後,會更容易了解程式中的流程
8.若完全沒使用過Youtube API,強烈建議先下載  youtube-api-samples
若有興趣可以先看一看這部教學影片  YouTube Developers Live: Setting up your Java IDE for Google API samples,這部影片是針對 sample code做講解,裡面的sample code使用 Maven做專案資源的下載,若想了解Maven請自行google,所以若Eclipes沒有整合Maven,也可以先在Eclipse Marketplace打Maven後便可以找到Maven整合Eclipse的安裝程式

開始開發工作:
1.我的開發工具是Java,所以我選擇sample code內Java底下的youtube-cmdline-createbroadcast-sample Project來測試實作
2.選擇 File->Import->Maven->Existing Maven Project
3.選擇youtube-cmdline-createbroadcast-sample Project的實際存放路徑後,在下方可以看到可選擇的pom.xml檔,勾選後按下Finish,Maven便會根據 pom.xml檔案將所需要的 lib資源從網路下載下來,毋須在自己去找專案相依的那些jar檔
4.完成下載後,我們的Maven Dependencies應該有如下的jar檔

5.完整source code
/*
Source code here

6.程式流程的第一件事情:應用程式的授權認證,這邊就必須使用到我們在準備工作所提到的用戶端ID用戶端密碼 ,假設各位已經在準備工作中取得授權的這兩樣東西,那麼請打開在src/main/resources底下的client_secrets.json檔案,分別將取到的ID與 密碼貼到對應的json欄位;下方的程式碼就可以透過這組ID與密碼,以及scopes等授權你的應用程式去對你在youtube account下不管是Channel或是LiveBroadCast(線上live頻道)做變動或讀取 ;

// Scope required to wrie data to YouTube.
  List<String> scopes = Lists
    .newArrayList("https://www.googleapis.com/auth/youtube");

  try {
// Authorization. 取得授權的憑證物件後,傳入新建立的youtube物件
Credential credential = authorize(scopes);

   // YouTube object used to make all API requests.
   youtube = new YouTube.Builder(HTTP_TRANSPORT, JSON_FACTORY,
     credential).setApplicationName(
     "youtube-cmdline-createbroadcast-sample").build();
7.授權完成後,開始建立一個LiveBroadcast物件,此物件可以想像成是一個線上廣播電台,需要有廣播電台名稱,以及開始時間,結束時間,電台的狀態等。相關屬性都設定好後,透過YouTube.LiveBroadcasts.Insert 類別insert,在這邊對insert method傳入的參數有兩個,part及 broadcast 物件,part 是指定要寫入的broadcast property,這邊指定寫入"snippet,status"兩個屬性,broadcast物件就是要建立的broadcast本體。
想了解更多關於此物件的相關屬性或使用方式,請參考LiveBroadcast API,同樣的,若想查詢其他物件的API,也可以在上方的查詢列打入物件名稱找尋
String title = getBroadcastTitle();
   System.out.println("You chose " + title + " for broadcast title.");

   // Create a snippet with title, scheduled start and end times.
   LiveBroadcastSnippet broadcastSnippet = new LiveBroadcastSnippet();
   broadcastSnippet.setTitle(title);
   
   broadcastSnippet.setPublishedAt(new DateTime(
     "2014-10-07T16:30:00.000+08:00"));
   broadcastSnippet.setScheduledStartTime(new DateTime(
     "2014-10-07T16:30:00.000+08:00"));
   broadcastSnippet.setScheduledEndTime(new DateTime(
     "2014-10-07T18:00:00.000+08:00"));

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

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

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

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

   // Print out returned results.
   System.out
     .println("\n================== Returned Broadcast ==================\n");
   System.out.println("  - Id: " + returnedBroadcast.getId());
   System.out.println("  - Title: "
     + returnedBroadcast.getSnippet().getTitle());
   System.out.println("  - Description: "
     + returnedBroadcast.getSnippet().getDescription());
   System.out.println("  - Published At: "
     + returnedBroadcast.getSnippet().getPublishedAt());
   System.out.println("  - Scheduled Start Time: "
     + returnedBroadcast.getSnippet().getScheduledStartTime());
   System.out.println("  - Scheduled End Time: "
     + returnedBroadcast.getSnippet().getScheduledEndTime());
8.電台建立好後,準備建立電台的廣播通道,也就是LiveStream物件,有這個通道,才能接收Live訊號流進來。建立方式如下程式碼:
// Get the user's selected title for stream.
   title = getStreamTitle();
   System.out.println("You chose " + title + " for stream title.");

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

   LiveStreamCdnIngestionInfo ingestionInfo = new LiveStreamCdnIngestionInfo();
   ingestionInfo.setStreamName("Education2");
   //此Rtmp位址為固定,不需改變
   //Youtube提供給我們打入Stream訊號的Rtmp Server
   ingestionInfo
     .setIngestionAddress("rtmp://a.rtmp.youtube.com/live2");

   // Create content distribution network with format and ingestion
   // type.
   LiveStreamCdn cdn = new LiveStreamCdn();
   cdn.setFormat("720p");
   cdn.setIngestionType("rtmp");
   cdn.setIngestionInfo(ingestionInfo);

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

   // Print out returned results.
   returnedStream.getCdn().getIngestionInfo().getStreamName();
   System.out
     .println("\n================== Returned Stream ==================\n");
   System.out.println("  - Id: " + returnedStream.getId());
   System.out.println("  - Title: "
     + returnedStream.getSnippet().getTitle());
   System.out.println("  - Description: "
     + returnedStream.getSnippet().getDescription());
   System.out.println("  - Published At: "
     + returnedStream.getSnippet().getPublishedAt());
   System.out.println("  - Stream Name: "
     + returnedStream.getCdn().getIngestionInfo()
       .getStreamName());
   System.out.println("  - Stream Address: "
     + returnedStream.getCdn().getIngestionInfo()
       .getIngestionAddress());
9.將前面所建立的broadcast物件與stream物件做綁定,一個broadcast只能有一個stream來源,而一個stream可以做為多個broadcast的來源,綁定的程式碼如下:
// 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
   returnedBroadcast = liveBroadcastBind.execute();

   // Print out returned results.
   System.out
     .println("\n================== Returned Bound Broadcast ==================\n");
   System.out
     .println("  - Broadcast Id: " + returnedBroadcast.getId());
   System.out.println("  - Bound Stream Id: "
     + returnedBroadcast.getContentDetails().getBoundStreamId());
10. 現在萬事皆備,只欠東風,相信現在的疑問就是:如何將我們的Live Stream流打至Youtube上,方法理論上應該只有一種,就是將我們Live Encoder的FMS(Flash Media Server)設成剛剛在程式中指定的rtmp://a.rtmp.youtube.com/live2 ,同時在  Live Encoder能夠設定 Stream Name的地方,設定Youtube回傳給我們的 stream物件的streamName,這兩點很重要,下面先列出我程式執行結果以方便說明:
Please enter a broadcast title: test1
You chose test1 for broadcast title.

================== Returned Broadcast ==================

  - Id: NqtWlfFOQb8
  - Title: test1
  - Description:
  - Published At: 2014-10-07T08:33:34.000Z
  - Scheduled Start Time: 2014-10-07T08:30:00.000Z
  - Scheduled End Time: 2014-10-07T10:00:00.000Z
Please enter a stream title: stream4
You chose stream4 for stream title.

================== Returned Stream ==================

  - Id: h5jJg5xHAzn4jSSf46IGcA1412670820085418
  - Title: stream4
  - Description:
  - Published At: 2014-10-07T08:33:40.000Z
  - Stream Name: jay12210922.bc0f-7pc1-6mb5-d59c =>提供給Live Encoder去設定StreamName
  - Stream Address: rtmp://a.rtmp.youtube.com/live2   =>提供給Live Encoer去設定FMS

透過從youtube回傳的stream物件,去設定我們使用的Live Encoder,這裡我所使用的Live Encoder為Adobe Flash Media Live Encoder 3.2,若沒有可以先去下載,若想模擬live stream,可使用ManyCam,下載下來安裝完,播放影片,就會直接被Adobe 的Live Encoder偵測到,產生live stream訊號。下圖附上我在Live Encoder的設定:
設定完成後,在下方按下Start,正常情況下會看到左下方的Streaming to Primary...,到這裡已經完成了將我們local端的stream流送至youtube的設定,底下再來看看youtube是否真的已經接收到stream流訊號

11.在程式的最後埋入不斷去偵測我們在youtube所建立的liveStream的狀態,當liveStream還沒收到任何訊號時,狀態會顯示ready,程式中是每隔五秒去偵測一次狀態是否改變為active,若改變為active,我們就知道liveStream物件已經收到訊號流了,此時可以開始測試我們的broadcast了。底下列出程式碼:
YouTube.LiveStreams.List liveStreamRequest = youtube
     .liveStreams()
     .list("id,status")
     .setId(returnedBroadcast.getContentDetails()
       .getBoundStreamId());

   LiveStreamList returnedList = liveStreamRequest.execute();
   List<LiveStream> liveStreams = returnedList.getItems();
   if (liveStreams != null && liveStreams.size() > 0) {
    LiveStream liveStream = liveStreams.get(0);

    while (!liveStream.getStatus().getStreamStatus()
      .equals("active")) {
     Thread.sleep(5000);
     returnedList = liveStreamRequest.execute();
     liveStreams = returnedList.getItems();
     liveStream = liveStreams.get(0);
    }
    //the broadcast cann't transition the status from ready to live directly,
    //it must change to testing first and then live.
    YouTube.LiveBroadcasts.Transition broadCastRequest = youtube
      .liveBroadcasts().transition("testing",
        returnedBroadcast.getId(), "id,contentDetails,status");

    LiveBroadcast liveBroadCast = broadCastRequest.execute();
    String youTubeLink = "https://www.youtube.com/watch?v="+liveBroadCast.getId();
    System.out.println("youTubeLink:"+youTubeLink);
    System.out.println("Broad EmbedHtml:"+liveBroadCast.getContentDetails().getMonitorStream().getEmbedHtml());
    System.out.println("Broad Cast Status:"+liveBroadCast.getStatus().getLifeCycleStatus());
   }
這裡再提示一點,要可以看到broadcast的內容,必須在broadcast的status為testinglive的時候,因此程式碼透過YouTube.LiveBroadcasts.Transition 去改變broadcast物件的狀態為testing,就是表示我們要開始測試這各廣播電台的stream通道是否有正常收到訊號;當testing沒問題時,要正式讓所有人都可以在youtube上看到我們的live broadcast內容時,只需再將broadcast的狀態改為live即可,我這邊的程式碼並沒有包含將狀態改為live,請各位自行動手實作。
下面附上狀態改變完,接收到的broadcast結果:
================== Returned Bound Broadcast ==================

  - Broadcast Id: NqtWlfFOQb8
  - Bound Stream Id: h5jJg5xHAzn4jSSf46IGcA1412670820085418
youTubeLink:https://www.youtube.com/watch?v=NqtWlfFOQb8 => 產生出來可在youtube上看live 轉播的網址
Broad EmbedHtml:<iframe width="425" height="344" src="http://www.youtube.com/embed/NqtWlfFOQb8?autoplay=1&livemonitor=1" frameborder="0" allowfullscreen></iframe> => 若要嵌入至某個網頁,則使用此html語法
Broad Cast Status:testStarting

最後附上一些參考的文件或網站,若還是有問題都可以多方參考,畢竟本人又是第一次做這個功能,一定有許多未發現的功能等著大家自行去探索!

我研究旅途的起點,看一些基本觀念跟追蹤資訊的入口

Streaming to YouTube Live? Put an API on it!

重要的API參考入口網址,裡面有"try  it"的地方都可以試著去執行他,經過這個階段可以更了解該API各參數的功用以及回傳的結果

了解Broadcast的生命週期,就更清楚如何去建立、控制Broadcast、改變Broadcast的狀態

live Encoder 的設定參數

若使用node.js的人,可以參考此篇實作

4 則留言:

  1. 請問這Youtube Live Streaming 的範例,可不可以用在Android手機上實做?
    讓手機的鏡頭與Youtube Live 做連結。

    回覆刪除
  2. D ream你好,很抱歉忙碌了一段時間到現在才來回覆。
    希望你已經找到你問題的答案,若還沒有,以下連結可提供你參考:
    http://youtube-eng.blogspot.tw/2014/10/watchme-live-stream.html
    我想答案是肯定的,但我也沒有在手機實作過,所以你可能還要再
    試一試

    回覆刪除
  3. 請問有可能在unity使用youtube livestreaming嗎
    youtube直播影片的m3u8路徑會一直更改,請問要怎麼讓UNITY 隨時可以抓取M3U8

    回覆刪除
  4. 不知道你所說的m3u8一直改,是不是指youtube會持續的發出像類似這樣的request
    https://r8---sn-ipoxu-un56.googlevideo.com/videoplayback?id=_IP5K1bB-4A.3&itag=134&source=yt_live_broadcast&requiressl=yes&playlist_type=LIVE&ei=S8iaV9TIA4ap4gL03b3ICg&gcr=tw&ratebypass=yes&cmbypass=yes&mime=video%2Fmp4&live=1&gir=yes&fexp=3300102,3300130,3300164,3313268,9416891,9418404,9419451,9422596,9428398,9431012,9433096,9433221,9433507,9433946,9435247,9435526,9435876,9437066,9437552,9438327,9438662,9438896,9439093,9439355,9439471,9439553,9439581,9439652,9439743,9439888,9440502,9440880,9441114,9441582,9442424,9443259&upn=rs9DtNwM-T8&signature=08346A50B718920C2473B4BCDB9C21C9737B89C0.0F5EDBB1DBC8EE6B2A2648838C506CCE696A0F49&sver=3&key=cms1&cpn=jn8AtstUgSdUEEop&mpd_version=4&ip=60.248.161.28&ipbits=0&expire=1469783211&sparams=cmbypass,ei,expire,gcr,gir,id,ip,ipbits,itag,live,mime,mm,mn,ms,mv,pl,playlist_type,ratebypass,requiressl,source&mm=32&mn=sn-ipoxu-un56&ms=lv&mt=1469761533&mv=m&pl=24&alr=yes&keepalive=yes&c=WEB&cver=1.20160727&sq=25&clen=35154&lmt=1469761686423474&dur=0.434&rn=23&rbuf=850,
    看起來youtube的播放器會持續的去request這個url來取得streaming data,其中的參數會不段的改變來取得不同的資料段, 這是youtube的streaming機制,至於多久request一次以及參數的變化,這部分我並沒有深入研究過;我認為如果你是想在UNITY中達到播放YOUTUBE直播的效果,是不是考慮在UNITY中內嵌瀏覽器,直接透過播放YOUTUBE直播連結的方式,會來的省力許多,希望這點小小意見對你有幫助。

    回覆刪除