Created
January 30, 2020 08:34
-
-
Save unitycoder/b3909ed0c485bb32167ab20afa49f380 to your computer and use it in GitHub Desktop.
Stream to Youtube from Unity?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://stackoverflow.com/questions/59971550/transmitting-the-video-to-youtube-streaming-api-v3-with-unity | |
{ | |
private Thread thread; | |
IEnumerator Start() | |
{ | |
// GOOGLE API KEYS | |
var clientId = ""; | |
var clientSecret = ""; | |
var code = ""; // LEAVE IT | |
// Login and get the token | |
LocalServer(c => code = c); | |
var authUrl = "https://accounts.google.com/o/oauth2/v2/auth?response_type=code" | |
+ "&client_id=" + clientId | |
+ "&redirect_uri=" + "http://localhost:8080" | |
+ "&scope=" + "https://www.googleapis.com/auth/youtube.force-ssl" | |
+ "&access_type=" + "offline"; | |
Application.OpenURL(authUrl); | |
yield return new WaitUntil(() => code != ""); | |
// Debug.Log(code); | |
var tokenUrl = "https://www.googleapis.com/oauth2/v4/token"; | |
var content = new Dictionary<string, string>() { | |
{ "code", code }, | |
{ "client_id", clientId }, | |
{ "client_secret", clientSecret }, | |
{ "redirect_uri", "http://localhost:8080" }, | |
{ "grant_type", "authorization_code" }, | |
{ "access_type", "offline" }, | |
}; | |
var request = UnityWebRequest.Post(tokenUrl, content); | |
yield return request.SendWebRequest(); | |
var json = JSON.Parse(request.downloadHandler.text); | |
var token = json["access_token"].RawString(); | |
// Insert the live broadcast | |
var liveBroadCastURL = "https://www.googleapis.com/youtube/v3/liveBroadcasts?part=snippet%2CcontentDetails%2Cstatus"; | |
// Create live broadcast json to send to the server | |
LiveBroadcast.Snippet liveBroadcastSnip = new LiveBroadcast.Snippet(); | |
liveBroadcastSnip.title = "Sample broadcast"; | |
liveBroadcastSnip.scheduledStartTime = "2020-01-29T10:07:48+0000"; | |
liveBroadcastSnip.scheduledEndTime = "2020-01-29T10:07:48+0000"; | |
LiveBroadcast.Status liveBroadcastState = new LiveBroadcast.Status(); | |
liveBroadcastState.privacyStatus = "public"; | |
liveBroadcastState.selfDeclaredMadeForKids = true; | |
LiveBroadcast.LiveBroadcastObject liveBroadcastData = new LiveBroadcast.LiveBroadcastObject(); | |
liveBroadcastData.snippet = liveBroadcastSnip; | |
liveBroadcastData.status = liveBroadcastState; | |
var liveBroadcastRequest = new UnityWebRequest(liveBroadCastURL, "POST"); | |
string broadcastData = JsonUtility.ToJson(liveBroadcastData); | |
byte[] broadcastBody = Encoding.UTF8.GetBytes(broadcastData); | |
liveBroadcastRequest.uploadHandler = new UploadHandlerRaw(broadcastBody); | |
liveBroadcastRequest.downloadHandler = new DownloadHandlerBuffer(); | |
liveBroadcastRequest.SetRequestHeader("Authorization", "Bearer " + token); | |
liveBroadcastRequest.SetRequestHeader("Content-Type", "application/json"); | |
yield return liveBroadcastRequest.SendWebRequest(); | |
// server response and save created broadcast id | |
var broadcastJson = JSON.Parse(liveBroadcastRequest.downloadHandler.text); | |
var broadcastID = broadcastJson["id"].RawString(); | |
UnityEngine.Debug.Log("Broadcast ID: " + broadcastID); | |
// Create live stream | |
var LiveStreamURL = "https://www.googleapis.com/youtube/v3/liveStreams?part=snippet%2Ccdn%2CcontentDetails%2Cstatus"; | |
// Create live stream json to send to the server | |
LiveStream.Snippet liveStreamSnip = new LiveStream.Snippet(); | |
liveStreamSnip.title = "Sample Test"; | |
liveStreamSnip.description = "Hello My Video"; | |
LiveStream.Cdn liveStreamCDN = new LiveStream.Cdn(); | |
liveStreamCDN.frameRate = "60fps"; | |
liveStreamCDN.ingestionType = "rtmp"; | |
liveStreamCDN.resolution = "720p"; | |
LiveStream.ContentDetails liveStreamDetails = new LiveStream.ContentDetails(); | |
liveStreamDetails.isReusable = false; | |
LiveStream.LiveStreamObject liveStreamData = new LiveStream.LiveStreamObject(); | |
liveStreamData.snippet = liveStreamSnip; | |
liveStreamData.cdn = liveStreamCDN; | |
liveStreamData.contentDetails = liveStreamDetails; | |
string streamData = JsonUtility.ToJson(liveStreamData); | |
var liveStreamRequest = new UnityWebRequest(LiveStreamURL, "POST"); | |
byte[] streamBody = Encoding.UTF8.GetBytes(streamData); | |
liveStreamRequest.uploadHandler = new UploadHandlerRaw(streamBody); | |
liveStreamRequest.downloadHandler = new DownloadHandlerBuffer(); | |
liveStreamRequest.SetRequestHeader("Authorization", "Bearer " + token); | |
liveStreamRequest.SetRequestHeader("Content-Type", "application/json"); | |
yield return liveStreamRequest.SendWebRequest(); | |
// server response and save created stream data | |
var streamJson = JSON.Parse(liveStreamRequest.downloadHandler.text); | |
var streamID = streamJson["id"].RawString(); | |
var streamName = streamJson["cdn"]["ingestionInfo"]["streamName"].RawString(); | |
var ingestionAddress = streamJson["cdn"]["ingestionInfo"]["ingestionAddress"].RawString(); | |
UnityEngine.Debug.Log("Stream ID: "+ streamID); | |
UnityEngine.Debug.Log("Stream Name: " + streamName); | |
UnityEngine.Debug.Log("Ingestion Address: " + ingestionAddress); | |
// Bind the stream and broadcast | |
var bindUrl = "https://www.googleapis.com/youtube/v3/liveBroadcasts/bind"; | |
var bindContent = new Dictionary<string, string>() { | |
{ "id", broadcastID }, | |
{ "part", "snippet" }, | |
{ "streamId", streamID }, | |
}; | |
var bindRequest = UnityWebRequest.Post(bindUrl, bindContent); | |
bindRequest.SetRequestHeader("Authorization", "Bearer " + token); | |
bindRequest.SetRequestHeader("Accept", "application/json"); | |
yield return bindRequest.SendWebRequest(); | |
var bindJson = JSON.Parse(bindRequest.downloadHandler.text); | |
var bindID = bindJson["id"].RawString(); // not importand or used | |
UnityEngine.Debug.Log("Bind ID: " + bindID); | |
Application.OpenURL("https://studio.youtube.com/channel/"+streamID+"/livestreaming/dashboard?v=" + broadcastID); | |
// Start the encoder | |
List<string> commands = new List<string>(); | |
commands.Add(@"cd " + Application.dataPath + "/Resources/FFmpeg"); | |
commands.Add("ffmpeg -re -i Sample.mp4 -c copy -f flv rtmp://a.rtmp.youtube.com/live2/" + streamName); | |
thread = new Thread(() => RunCommands(commands)); | |
thread.Start(); | |
} | |
void LocalServer(Action<string> onReceive) | |
{ | |
ThreadStart start = () => { | |
try | |
{ | |
var listener = new HttpListener(); | |
listener.Prefixes.Add("http://*:8080/"); | |
listener.Start(); | |
var context = listener.GetContext(); | |
var req = context.Request; | |
var res = context.Response; | |
var re = new Regex(@"/\?code=(?<c>.*)"); | |
var code = re.Match(req.RawUrl).Groups["c"].ToString(); | |
onReceive(code); | |
res.StatusCode = 200; | |
res.Close(); | |
} | |
catch (Exception e) | |
{ | |
UnityEngine.Debug.LogError(e); | |
} | |
}; | |
new Thread(start).Start(); | |
} | |
public static void RunCommands(List<string> cmds, string workingDirectory = "") | |
{ | |
var process = new Process(); | |
var psi = new ProcessStartInfo(); | |
psi.FileName = "cmd.exe"; | |
psi.RedirectStandardInput = true; | |
psi.CreateNoWindow = true; | |
psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; | |
psi.RedirectStandardOutput = true; | |
psi.RedirectStandardError = true; | |
psi.UseShellExecute = false; | |
psi.WorkingDirectory = workingDirectory; | |
process.StartInfo = psi; | |
process.Start(); | |
process.OutputDataReceived += (sender, e) => { UnityEngine.Debug.Log(e.Data); }; | |
process.ErrorDataReceived += (sender, e) => { UnityEngine.Debug.Log(e.Data); }; | |
process.BeginOutputReadLine(); | |
process.BeginErrorReadLine(); | |
using (StreamWriter sw = process.StandardInput) | |
{ | |
foreach (var cmd in cmds) | |
{ | |
sw.WriteLine(cmd); | |
} | |
} | |
process.WaitForExit(); | |
RunCommands(cmds); | |
} | |
private void OnApplicationQuit() | |
{ | |
thread.Abort(); | |
} | |
} | |
public static class SimpleJsonUtility | |
{ | |
public static string RawString(this JSONNode node) | |
{ | |
var len = node.ToString().Length - 2; | |
return node.ToString().Substring(1, len); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment