This add-on is operated by Telestream LLC
Video encoding service with a pretty, yet powerful API
Telestream Cloud
Last updated July 27, 2023
This article is a work in progress, or documents a feature that is not yet released to all users. This article is unlisted. Only those with the link can access it.
Table of Contents
We provide a Javascript uploader that you embed on a page in your application and sends the video directly to Panda, an encoder that converts it to any format you like, and a REST API to let you manage videos.
Prerequisites
Panda uses Amazon S3 buckets to store encoded videos. Before you can use the add-on, you’ll need your Amazon Access Key ID and Amazon Secret Key. You can find these in the Security Credentials section of your Amazon account. If you don’t have one, you can sign up for an S3 account.
This guide assumes that you’re using Ruby, although Panda will still work with other Heroku-deployable applications.
Add the Heroku add-on
The free plan allows you to upload and encode an unlimited number of videos. However the filesize of uploads must be less than 10Mb. For production usage there a several paid plans available with no upload limit and dedicated encoders.
To install the free version of the Panda add-on, simply run:
$ heroku addons:create pandastream
Adding pandastream:sandbox to myapp... done
You can also do this from the Resources section of your application’s configuration page on Heroku.
When you add your Heroku addon, It automatically creates a new Pandastream account which is only available from Heroku. You should not signup to the website directly.
Running this command will set the Heroku configuration variable PANDASTREAM_URL
. You can see it by typing heroku config --long
.
Install the gem
Add the Panda gem to your Gemfile
so that it will be loaded.
gem 'panda', '~> 1.6.0'
Next install it:
$ bundle install
Finally, create the file config/initializers/panda.rb
with the line below so that your application connects to Panda when it starts:
Panda.configure(ENV['PANDASTREAM_URL'])
Configure Panda
Before using Panda in your application, you need to let Panda know where to save encoded videos.
$ heroku addons:open pandastream
Click on Settings and enter your S3 bucket and credentials.
Set up locally
ENV['PANDASTREAM_URL']
isn’t set locally by default. By copying the connection information to config/environments/development.rb
you will be able to use your Heroku Panda account in development mode locally.
$ heroku config
PANDASTREAM_URL => http://a8704c6c854d69a031d8:efe2c61c3edfc7772556@api.pandastream.com:443/927a9d9xk37ded62422d4613229c156f
Copy the connection url to config/environments/development.rb
:
ENV['PANDASTREAM_URL'] = "http://a8704c6c854d69a031d8:efe2c61c3edfc7772556@api.pandastream.com:443/927a9d9xk37ded62422d4613229c156f"
Test your setup
If you’ve set up your local development environment with your credentials, you can test on your local console by running rails c
.
Fire up a console:
$ heroku run console
The initializer should connect you to Panda automatically, and you should be able to list all your encoding profiles.
Panda::Profile.all
=> [<Panda::Profile preset_name: h264, ...>]
Panda encodes your videos to a standard MP4 (H264, AAC) profile by default, but you can set up Encoding Profiles to output whichever format you like through the web interface or the API.
You can now upload your videos to panda providing a source url or a local file. We have also made available a sample video for your convenience.
# import a file to Panda
video = Panda::Video.create(:source_url => "http://panda-test-harness-videos.s3.amazonaws.com/panda.mp4")
# upload a local file
video = Panda::Video.create(:file => "/local/path/panda.mp4")
Now wait until the video has finished encoding (which could be several minutes depending on the size). You can check by doing:
video.reload.status
=> "success"
Your input video has been uploaded to s3
video.encodings['h264'].reload
video.encodings['h264'].encoding_progress
=> 100
video.encodings['h264'].status
"success"
Now you can get the URL of a re-encoded version:
video.encodings['h264'].url
=> "http://s3.amazonaws.com/S3_BUCKET/e40c1f68fbc1c4db46.mp4"
Open this URL in your browser, and you’ll see that Panda has received, re-encoded, and stored your video into your S3 bucket.
Everything’s working nicely. Time to integrate with your application!
Integrating with Rails
First we need to setup our models and controllers.
Create a Video model to save the ID of the video from Panda, we’ll call it panda_video_id
:
rails g model Video title:string panda_video_id:string
rake db:migrate
Panda provides its own Panda::Video model, which we’ll wrap with our ActiveRecord model. Edit app/models/video.rb
:
class Video < ActiveRecord::Base
validates_presence_of :panda_video_id
def panda_video
@panda_video ||= Panda::Video.find(panda_video_id)
end
end
Now you can access the wrapped object with myvideo.panda_video
, or go directly to the encodings with myvideo.panda_video.encodings
. This call requires a call to the Panda API, so we cache it with an instance variable to save time.
We’ll use a simple VideosController following the REST pattern. Create app/controllers/videos_controller.rb
:
class VideosController < ApplicationController
def show
@video = Video.find(params[:id])
@original_video = @video.panda_video
@h264e = @original_video.encodings["h264"]
end
def new
@video = Video.new
end
def create
@video = Video.create!(params[:video])
redirect_to :action => :show, :id => @video.id
end
end
Next we need to create an upload form.
Create or edit your layout /app/views/layouts/application.html.erb
, and include the uploader.
<script src="//cdn.pandastream.com/u/2.0/panda-uploader.min.js"></script>
Now create a view which will be the video upload form: /app/views/videos/new.html.erb
.
<%= form_for @video do |f| %>
<!-- field where the video ID will be stored after the upload -->
<input type="hidden" name="video[panda_video_id]"/>
<label>Title</label>
<input type="text" name='video[title]' placeholder="Give a title">
<!-- optional upload progress bar -->
<div class='progress'><span id="progress-bar" class='bar'></span></div>
<!-- file selector -->
<div id="browse">Choose file</div>
<% end %>
<script>
panda.uploader.init({
'buttonId': 'browse',
'progressBarId': 'progress-bar',
'onSuccess': function(file, data) {
$("#panda_video_id").val(data.id)
},
'onComplete': function(){
$("#new_video").submit();
}
});
</script>
Before you can upload a video you’ll need to authenticate and reserve an upload url for each file. The authentication process of your new upload occurs via an HTTP request to a configurable authentication url when the file is ready to be uploaded. The HTTP request is executed via AJAX POST request. The destination of the authentication request can be configured by setting the authorizeUrl.
The default route is /panda/authorize_upload
# app/controllers/panda_controller.rb
class PandaController < ApplicationController
def authorize_upload
payload = JSON.parse(params['payload'])
upload = Panda.post('/videos/upload.json', {
file_name: payload['filename'],
file_size: payload['filesize'],
profiles: "h264",
})
render :json => {:upload_url => upload['location']}
end
end
Next we need to create a basic view to display the video once uploaded. For this we will simply use an HTML5 player.
Edit your show page in app/views/videos/show.html.erb
:
<% if @h264e.status == "success" %>
<video id="movie" width="<%= @h264e.width %>" height="<%= @h264e.height %>" preload="none"
poster="<%= @h264e.screenshots.first %>" controls>
<source src="<%= @h264e.url %>" type="video/mp4">
</video>
<% else %>
Your video is processing: <%= @h264e.encoding_progress.to_i %>%
<% end %>
Finally, update your routes in config/routes.rb
.
match "/panda/authorize_upload", :to => "panda#authorize_upload"
resources :videos
root :to => "videos#new"
Your upload page is now ready. Once the video has been encoded, refresh the page and you’ll see the video!
You can view the code of the complete example on github.
Using WebHooks
Polling data from Panda is not very efficient but very simple to implement at first. A more efficient way to do it, is to use Panda webhooks and Async workers (In this example, we are going to use Sidekiq)
class PandaController < ApplicationController
def notifications
if params['event'] == 'video-encoded'
video = Video.find_by_panda_video_id(video_id)
if video
UpdateVideoState.perform_async(video.id)
else
# might have been deleted
end
end
end
end
class UpdateVideoState
include Sidekiq::Worker
def perform(id)
video = Video.find(id)
if video.panda_video.status == 'fail'
# video failed to be uploaded to your bucket
else
h264e = video.panda_video.encodings['h264']
if h264e.status == 'success'
# save h264e.url in your models to avoid querying panda each time
else # handle a failed job
# h264e.error_class; h264e.error_message
# a log file has been dumped in your bucket
end
end
end
end