Skip to content

Instantly share code, notes, and snippets.

@unitycoder
Created January 30, 2020 08:34
Show Gist options
  • Save unitycoder/b3909ed0c485bb32167ab20afa49f380 to your computer and use it in GitHub Desktop.
Save unitycoder/b3909ed0c485bb32167ab20afa49f380 to your computer and use it in GitHub Desktop.
Stream to Youtube from Unity?
// 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