api.video
SearchCtrl K

Features

Developers

False start: showing the video playback url before the video is ready

September 7, 2021 - Doug in video create, Video upload, webhooks, JavaScript, NodeJS

When our API has completed a video upload, the response contains all of the URLs you will need for playback:

"assets": {
          "iframe": "<iframe src=\"https://embed.api.video/vod/vi4blUQJFrYWbaG44NChkH27\" width=\"100%\" height=\"100%\" frameborder=\"0\" scrolling=\"no\" allowfullscreen=\"\"></iframe>",
          "player": "https://embed.api.video/vod/vi4blUQJFrYWbaG44NChkH27",
          "hls": "https://cdn.api.video/vod/vi4blUQJFrYWbaG44NChkH27/hls/manifest.m3u8",
          "thumbnail": "https://cdn.api.video/vod/vi4blUQJFrYWbaG44NChkH27/thumbnail.jpg",
          "mp4": "https://cdn.api.video/vod/vi4blUQJFrYWbaG44NChkH27/mp4/1080/source.mp4"
      },

Of course - the video has only just finished uploading. It needs to be transcoded into a video stream for playback. If you try to open the player URL, you'll get an error saying the video is not ready yet.

It's just like a false start in a race - you're ready to go - and you want to show the video to your users, but you show the link too soon - and it doesn't work. Disappointing all around. :(

You can see this for yourself at upload.a.video. Upload a large file, and then click the "watch here" link. If you're fast enough, you'll get the error. (Our encoding keeps getting faster though!!)

No more errors

No one wants to see an error. And we want to show off your new video to everyone ASAP. In order to ensure that the video is ready to play back (and avoid the false start), we can use api.video's webhooks. More specifically, we're interested in the video.encoding.quality.completed notification.

The response for this notification looks something like:

{ "type": "video.encoding.quality.completed", 
"emittedAt": "2021-01-29T16:46:25.217+01:00",
"videoId": "viXXXXXXXX", 
"encoding": "hls",
"quality": "720p"}

and for each video uploaded, you'll get a webhook alert for each version of the video created. This adds a new wrinkle (or a new feature) to how we can display the video:

  • As fast as possible (and damn the torpedoes!): Each video size is released as soon as it is encoded. Since the 240p video is the smallest video (in terms of pixels and KB), it is typically the first video encoded. With this option, as soon as the 240p version of the video is encoded - play the video. The downside is of course that the quality at 240p is less than ideal on most screens.
  • Wait for the nicest version: Only release the video when the 1080p version is released. That way, you know that your viewers will have the best quality experience (but being a much larger video - it may take more time).
  • Goldilocks: Find the balance in the middle: not too fast/not too slow. Not the lowest quality/not the highest quality. Perhaps the 480p version of the video is a good balance point: it'll look OK, won't take too long, and the 720p 1080p/4k versions are not far behind.

Implementing your webhook

You can test this integration at upload.a.video/webhook.

First we register a webhook at api.video to report to: upload.a.video/receive_webhooks

Then we add this code to process the webhook result when it arrives:

app.post("/receive_webhook", function (request, response) {
	console.log("new video event from api.video");

	//we're only getting video.encoding.quality.completed right now.. but let's be careful in case the webhook changes

	if (type =="video.encoding.quality.completed"){
	  let videoId = body.videoId;
	  let encoding = body.encoding;
	  let quality = body.quality;
	  liveStreamId = body.liveStreamId;
	  webhookResponse = {"event":type, 
	  					"emittedAt": emittedAt, 
						"videoId":videoId,
						"encoding":encoding,
						"quality": quality
						}
	  
	} 
	webhooks.push(webhookResponse);
	response.sendStatus(200);  
  });

This endpoint takes the webhook and adds it to an array.

Video upload page

We've made 2 changes to the video upload page:

Add a encoding selector

We add a selector in the form to allow to you pick the level of encoding you desire to release the video. By default, we've selected 720p - on the higher end of things.

Change how the video url is displayed

When the video is uploaded, the earlier versions of the upload demos just added the link to the video (the false start!). We'll change the JavaScript on the page to do two things:

  1. Update the page that the video is being encoded, and the URL will be available shortly.
  2. Call the webhook endpoint to find out when the video encoding is completed.
console.log(video);
					videoId = video.videoId;
					playerUrl = video.assets.player;
					//we have the video URL, but it miight not be encoded yet. We need to get the webhook confirmation

					//STEP 1 tell them upload complete
					//hack to make the math look good.  yeah. i know.
					document.getElementById("chunk-information").innerHTML = "Last chunk"  + " is " + "100" + "% uploaded. Total uploaded: " + "100" +"%";
					document.getElementById("video-information").innerHTML = "all uploaded! We're now waiting for the server to tell us that the encoding is ready to play." ;
					//step 2 - start a new request for the webhook:
					var oReq = new XMLHttpRequest();


					oReq.open("POST", '/webhook', true);
					var qualityJson = {"quality":videoQuality,
										"videoId": videoId};
					console.log(qualityJson);
					var qualityJsonstring = JSON.stringify(qualityJson);
					oReq.onload = function (oEvent) {
						//encoding at selected quality is completed - tell the customer!!!
						console.log("Video encoded! Watch here: ",playerUrl ) ;
						document.getElementById("video-information").innerHTML = "Your Video has been encoded and is <a href=\'" + playerUrl +"\' target=\'_blank\'>ready to watch!</a>" ;
					}
					//send the quality value and the videoId we want to check on...
					oReq.send(qualityJsonstring);

So now the webpage is waiting for the Node JS server to say "hey the video encoding you are looking for is ready."

Node JS code to check encoding versions

Recall that the webhook is writing every videoId and encoded version into an array. So we can just cycle through that array every 2 seconds to see if there is a video with the videoId and the encoding quality present. Once the webhook adds the video/quality - this will read that it is ready, and then reply back to the website that it is safe to show the URL to the user:

app.post('/webhook',(req,res) =>{
	var reqBody = JSON.parse(req.body);

	//video uploaded -but check to see if the 
	var videoId = reqBody.videoId;
	var videoQuality  = reqBody.quality;
	console.log("received  " + videoId + " " +videoQuality);
	//we need to see if this videoId and qulity have been encoded.
	//loop through all the webhook responses
	
	function checkWebhook(videoId, videoQuality, webhooks){
		foundMatch = false;
		console.log("there are " + webhooks.length + " webhook entries to scan");
		
		
		for(var i=0;i<webhooks.length;i++){
				if(webhooks[i].videoId === videoId && webhooks[i].quality === videoQuality){
					//we have a match!!
					foundMatch = true;
					res.sendStatus(200); 				

				}
			//}
		}
		if(!foundMatch){
			//no match yet, so wait 2 seconds and try again
			//not encoded yet, wait 2 sec and re-reun checkMp4
			console.log("no webhook yet.");
			setTimeout(checkWebhook,2000,videoId, videoQuality, webhooks);
		}
	}
	checkWebhook(videoId, videoQuality, webhooks);

})

Conclusion

That's all there is to it. Instead of just showing the URL immediately after upload, we wait until the webhook tells us that the video is ready for playback. We can then provide the URL to our users (including the person who uploaded the video), and the link works - with "please wait while the video encodes" message.

Try it out at upload.a.video/webhook, and then use the code on Github to create your own webhook alerting function for videos.

Doug

Head of Developer Relations

Connect your users with videos

Create a free account today