Tech Insights
Asitha Bandara
June 6, 2019

A seamless viewing experience with Ruby on Rails

A seamless viewing experience with Ruby on Rails

This article is about an issue I struggled to solve for a long time. Iam pretty excited to share the solution with you.

Contents

1. Existing Architecture with Carrierwave GEM

2. The Issue I faced

3. What is FFMPEG?

4. What I did to fix the issue

Existing Architecture with Carrierwave GEM

The application I am talking about today has an API which receives mediacontent from mobile devices. Once received it should be saved in the server and also generate the following items from the media type “VIDEO”.

1. Raw video file

2. Scaled video file (less size, quality)

3. Thumbnail image

I managed to make this feature succesful with the following gems.

# Carrierwave
gem'carrierwave-video'
gem'carrierwave-video-thumbnailer'
gem'carrierwave_backgrounder'
gem'streamio-ffmpeg', '~> 2.0.0'
#Delayed job used by carrierwave_backgrounder
gem'delayed_job_active_record'

Basically I have created a pretty easy architecture on this:

This article is about an issue I struggled to solve for a long time. I am pretty excited to share the solution with you.

Contents

1. Existing Architecture with Carrierwave GEM

2. The Issue I faced

3. What is FFMPEG?

4. What I did to fix the issue

Existing Architecture with Carrierwave GEM

The application I am talking about today has an API which receives media content from mobile devices. Once received it should be saved in the server and also generate the following items from the media type “VIDEO”.

1. Raw video file

2. Scaled video file (less size, quality)

3. Thumbnail image

I managed to make this feature succesful with the following gems.

# Carrierwave
gem'carrierwave-video'
gem'carrierwave-video-thumbnailer'
gem'carrierwave_backgrounder'
gem'streamio-ffmpeg', '~> 2.0.0'
#Delayed job used by carrierwave_backgrounder
gem'delayed_job_active_record'

Basically I have created a pretty easy architecture on this:

My CarrierwaveVideo Model looks like this

 

<script src="https://gist.github.com/AsithaBandara/641e995f6a1bbc6afb70d4404108928b.js"></script>

Mounted Video Uploader from CarrierwaveVideo gem looks like this

view rawvideo_uploader.rb hostedwith ❤ by GitHub

Actually, this works well. However, this is the problem I faced. Once a video is uploaded it should look like below.

Nevertheless, when the video is uploaded from an Android device, there-scaled video looks like below (after encoding)

The video process (encoding) works nicely for the iOS-device-generated video, but not for the android generated ones. Even though I changed the encoding attributes it didn’t fix the problem. Some of the attributes I usedare;

PROCESSED_DEFAULTS = {
   resolution:            '500x400',
   preserve_aspect_ratio: :height.
   video_codec:           'libx264', # H.264/MPEG-4 AVC videocodec
   constant_rate_factor:  '30', # GOP Size
   frame_rate:            '25', # frame rate
   audio_codec:           'aac', # AAC audio codec
   audio_bitrate:         '64k', # Audio bitrate
   audio_sample_rate:     '44100' # Audio sampling frequency

When I was digging in to this issue, I found out that this process happens from a library called “FFMPEG”. And it is embedded with carrierwave:video gem to perform those video process features.

What is FFMPEG?

A complete, cross-platform solution to record, convert and stream audio and video. Check more info here.

Install FFMPEG using following commands (Ubuntu).

sudo apt-get update
sudoapt-get install ffmpeg

If you want to check it was a succesful installation

ffmpeg -version

This is my FFMPEG version

You can convert videos easily using the following commands once you successfully install FFMPEG.

ffmpeg -i some-video.mp4 -filter:v
se,pad=640:480:(ow-iw)/2:(oh-ih)/2"output.mp4

All encoding attributes can be included between the input video and the output video, as below

"scale='min(640,iw)':min'(480,ih)':force_original_aspect_ratio=decrea
se,pad=640:480:(ow-iw)/2:(oh-ih)/2"

When I tried this, the console looks like this.

When I converted the video, it was stretched. Only with Carrierwave using FFMPEG it worked fine. All were good. So, what went wrong here, was withcarrierwave gem. However, instead of looking for the issue with carrierwave gem, I added FFMPEG inside the class and used those methods.

What I did to fix the issue

I called a different method to encode the video instead of the encoding mechanism in the carrierwave.

version :rescaled do
process:encode
end

And the ENCODE method will create ffmpeg video object and take care of the transcode.

view rawencode.rb hostedwith ❤ by GitHub

This would override the processes from carrierwave and do the encoding with the ffmpeg. Don’t forget to incldue the ffmpeg library on top of theclass.

require 'streamio-ffmpeg'

That’s all then. Please don't forget to mention any solutions you find any.