[SOLVED] Backend Service: Broadcast to PubSub from Backend using Auth.Token JWT

For OBS to broadcast, all it needs is the streaming key from the user.

My extension backend service is an app. And it would be great if the broadcaster only needed to enter the streaming key into a password field to authorize broadcasting.

I need my extension backend service to broadcast to PubSub.

Is this possible? And what API would I use?

The extension backend service is C#.

So far I know it’s possible to open the PubSub connection on a WebSocket.

The API I’m looking for is authorizing using the stream key.

I’m looking for the API that lets me do this.

window.Twitch.ext.send(‘broadcast’, ‘application/json’, ‘{“message”: “hello”}’);

Broadcasting to pubsub and broadcasting video are two completely different things. Pubsub sends bundles of text over websockets while broadcasting video involves sending video data using a protocol called RTMP to an ingest server.

I was just admiring the broadcast video API how it has the stream key which is all the info the user has to enter to begin broadcasting.

I was hoping PubSub could use the same stream key to authenticate and broadcast to the PubSub.

That would be called an oauth token.

I ended up getting it working per the docs.

The extension auth,token is passed to the extension backend.

The backend parses the auth.token from the viewer auth callback to get the JWT info.

The backend broadcasts data to the PubSub via a POST.

// C#
public void SendFromExtensionBackEnd()
        {
            try
            {
                if (string.IsNullOrEmpty(_mAuthToken))
                {
                    return;
                }

                string[] parts = _mAuthToken.Split(".".ToCharArray());
                string str = parts[1];
                int mod4 = str.Length % 4;
                if (mod4 > 0)
                {
                    str += new string('=', 4 - mod4);
                }
                byte[] data = Convert.FromBase64String(str);
                string json = Encoding.UTF8.GetString(data);
                JObject jwt = JObject.Parse(json);
                if (null != jwt &&
                    null != jwt["channel_id"])
                {
                    string url = string.Format("https://api.twitch.tv/extensions/message/{0}",
                        jwt["channel_id"].ToString());

                    JObject sendBody = CreateSendBody(importantInfo);
                    if (null != sendBody)
                    {
                        string sendContent = sendBody.ToString();
                        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
                        request.Method = "POST";
                        request.ContentType = VALUE_CONTENT_TYPE;
                        request.Headers[HEADER_AUTHORIZATION] = string.Format("{0}{1}",
                            VALUE_AUTHORIZATION,
                            _mAuthToken);
                        request.Headers[HEADER_CLIENT_ID] = VALUE_CLIENT_ID;
                        byte[] bytes = Encoding.UTF8.GetBytes(sendContent);
                        request.ContentLength = bytes.Length;
                        using (Stream stream = request.GetRequestStream())
                        {
                            stream.Write(bytes, 0, bytes.Length);
                            stream.Flush();
                            stream.Close();
                        }
                        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                        if (null != response)
                        {
                            response.Close();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.Error.Write(ex);
                Console.Error.WriteLine();
            }
        }

And then preparing the body I’m doing a little extra by compressing the message on the backend.

        const string HEADER_AUTHORIZATION = "Authorization";
        const string VALUE_AUTHORIZATION = "Bearer ";
        const string VALUE_CONTENT_TYPE = "application/json";
        const string HEADER_CLIENT_ID = "Client-Id";

        public JObject CreateSendBody(string message)
        {
            byte[] bytes = Encoding.ASCII.GetBytes(message);
            using (MemoryStream ms = new MemoryStream())
            {
                using (ZipOutputStream zipStream = new ZipOutputStream(ms))
                {
                    zipStream.SetLevel(9);

                    ZipEntry zipEntry = new ZipEntry("a.json");
                    zipEntry.DateTime = DateTime.Now;
                    zipStream.PutNextEntry(zipEntry);

                    zipStream.Write(bytes, 0, bytes.Length);
                    zipStream.Flush();
                    zipStream.CloseEntry();
                    zipStream.IsStreamOwner = false;
                    zipStream.Close();

                    ms.Position = 0;
                    bytes = ms.ToArray();
                }
            }

            string compressed = Convert.ToBase64String(bytes);

            JObject jobject = new JObject();
            jobject.Add("content_type", VALUE_CONTENT_TYPE);
            jobject.Add("message", compressed);
            JArray targets = new JArray();
            targets.Add("broadcast");
            jobject.Add("targets", targets);
            return jobject;
        }

I updated the decompression library on the frontend to use this.

https://stuk.github.io/jszip/

The backend compresses with SharpZipLib with max compression (9).

1 Like

I intend to replace the SharpZipLib compression with Pako.

SharpZipLib is giving me ~ 7kb per update.

Pako was giving me 5kb per update.

JS:

C#:

            bytes = CompressZip(bytes); //6.7kb
            //bytes = CompressGZip(bytes); // 6.7kb
            //bytes = CompressZLib(bytes); //6.6kb
            //bytes = CompressTar(bytes); //no compression
            //bytes = CompressBzip2(bytes); //6.2kb

I solved the compression issue. With SharpZipLib compression, the compressed bytes are 5kb. But when the message is sent the bytes are Base64 encoded which bloats them by 75%.

The data was full of RGB color integer arrays. When I broke apart the RGB components into red, green, blue integers, it allowed the compression to do a better job.

And now the final size with Base64 encoding is 5kb.