Securing Videos on your sites using AWS ElasticTranscoder and HLS — Part 4
Part 1 — A Summary
Part 2 — Configuring Amazon S3 and Amazon Elastic Transcoder
Part 3 — Configuring Amazon Cloudfront to serve secure content
Part 4 — Serving Secure Content to Users
In the previous post, we went over configuring our Cloudfront Distribution to serve content from our hls-medias bucket.
In this post, we will be wrapping up and write code to finally start serving content to our users
Since I’m using Ruby on Rails, I will be illustrating this method using a gem. If you are using a different language, you will have to lookup a signed using your programming language. It should be very easy to do.
Sign URLs using aws_cf_signer gem
This gem is used to sign URLs and is available on github. Once installed, we can use this code to generate signed URLs for our private files. We will be creating signed URLs for the media present in our root folder.
Click on any media and copy its path. Click on the cloudfront distribution and copy its domain name, substitute the s3 domains with the cloudfront domains. Your URL should look something like this.
abcdefghi.cloudfront.net — Cloudfront Distribution URL
HLS/GothicxAvenue.m3u8 — Master Playlist Path
Result : https://abcdefghi.cloudfront.net/HLS/GothicxAvenue.m3u8
# get the resultant url and put it in a variable
media_url = ‘https://abcdefghij.cloudfront.net/HLS/GothicxAvenue.m3u8'# Use the AWS Cf Signer Gem and refer to it the cloudfront key we have generated earlieraws_signer = AwsCfSigner.new(Rails.root.join(‘public/external-files/pk-APKAIT7ACLXXXXXXXXX.pem’).to_s)# Generate the signed URL using these options. Refer to the github page of the gem for more info.
@signed_url = aws_signer.sign(url, :ending => (Time.now.in_time_zone(“Chennai”) + 30.minutes))
We save the url into an instance variable called @signed_url.
This signed URL is generated which we can use to access our private files. It should look something like this.
https://abcdefij.cloudfront.net/HLS/GothicxAvenue.m3u8?Expires=1555053731&Signature=Mv-kpbDHF88EdCxY4-iN9vZRruNM5IhvTVEWxIzqEYkXlHmbDKI4EXqWggL-TWwvITFGXbPHeD6JfvQ~4Bzm7lJxrxYPmafHS~W6sUth9dNViqE8KWgSIq4IGc6LM2hjmruHvnGHfPPD2Woh4QSicIIDyurzfiZFhDSzpQkimk3emO5G0JSgctN-SHTJX7O9jkwAeNwIIY4ETLPQck6Nhh5Vb-friDBQQ4ZS5SNZBCa-nNdzeZTFZ3xTDPKIiAeisOhzEdxi4EDpPoMw7R--A~lGmZaRVZ7Zi~cdAIltW3yRCWwamWRAYnX4tORnro-iITQwySZ5OaSpw-kv9PBC7g__&Key-Pair-Id=APKAIT7ACLXXXXXXXX
Collect Encryption Key from Elastic Transcoder
Head over to Amazon Elastic Transcoder. Go to Jobs and search for the job by issuing the pipeline name. Click on the photo icon to see Job details.
In the Outputs section, we can see the lock icon on the master playlist file. Hover over it and copy the encryption key.
This key needs to be decrypted and served from our get_secure_key endpoint.
Write get_secure_key action
Use a controller with GET action mapped to /hls/get_secure_key
def get_secure_key
kms = Aws::KMS::Client.new(region: ‘us-east-1’)
key = ‘AQIDAHiWz7W2xHHbu7GfOTUbgRdRxHzeK8I9hm8OXtDPz32mwwGbnY8yR2RCU4fIhKUJB28lAAAAbjBsBgkqhkiG9w0BBwagXzBdAgEAMFgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMGJkRwfRf4oAIJ4OBAgEQgCtoaFQjD4tzdQyaSvZmsV1unAjiwSKSWeLXzEX4mrDmrV3vQAq3Sb+2g4cm' # The key is base 64 encoded. Decrypt the key.
decoded = Base64.decode64(key)
data = {ciphertext_blob: decoded, encryption_context: {service:’elastictranscoder.amazonaws.com’}} # Send the base64 decoded key to kms.decrypt function to received the decoded data key.
final = kms.decrypt(data)
render :text => final.plaintext
end
Start Playing!
We still have a catch here though, HLS is not supported natively on Chrome, Mozilla etc. It is only supported on Safari.
We use the video playing library called hls.js which adds this functionality to Chrome and Mozilla
<script src=”https://cdn.jsdelivr.net/npm/hls.js@latest"></script><video id=”video” controls></video><input type=”hidden” id=”url” value=”<%= @signed_url %>”><script>var video = document.getElementById(‘video’);var url = document.getElementById(‘url’).value;if(Hls.isSupported()) {var config = {};var hls = new Hls(config);hls.loadSource(url);hls.attachMedia(video);hls.on(Hls.Events.MANIFEST_PARSED,function() {video.play();});}else if (video.canPlayType(‘application/vnd.apple.mpegurl’)) {video.src = url;video.addEventListener(‘loadedmetadata’,function() {video.play();});}</script>
This video will be now able to played on all browsers now!
Thanks for reading my AWS ElasticTranscoder and HLS series, I hope the posts were clear enough for you to implement them in your own systems.
In case you find any discrepancies, feel free to point it out and I will fix those mistakes.