Hardware Video Encoding progess with the Raspberry Pi

raspberryPi640_broadcom_webH_264_logo

***Scroll to bottom of article to see updated code with audio & video trancoding.*** 

The Raspberry Pi is not bad at hardware H264 encoding. It processed a 5.81GB 720P 60fps Mpeg Transport Stream that was an 1hr, 2min, 27seconds long and converted to H264 1280 x 720 60fps in 1hr, 38min, 57seconds with a resulting file size of 293MB.

The quality is a bit less than I’m used to with Handbrake and this is video only (still need to find out how to add audio to gstreamer transcoding). However for the quality it’s at it is an impressive speed for something the size of a credit card that consumes 5V1A of power and costs only $35.

Here’s how I was able to get the Raspberry Pi to get this far.

I installed an OpenMax enabled version of gstreamer from here:

http://theiopage.blogspot.com/2013/04/enabling-hardware-h264-encoding-with.html

I changed the video RAM split on the RPi to give it 128MB (I run the RPi headless from SSH so I had video RAM set at 16MB and received errors that the encoder wouldn’t start).

I used this code in terminal to transcode input file Gotham.2015-01-19.20-00.ts to output file Gotham.m4v

gst-launch-1.0 filesrc location=/home/pi/Gotham.2015-01-19.20-00.ts ! tsdemux ! mpegvideoparse ! omxmpeg2videodec ! videoconvert ! omxh264enc ! h264parse ! avimux ! filesink location=/home/pi/Gotham.m4v

I’m also fairly certain that this line “omxmpeg2videodec” requires the MPEG2 codec installed which I purchased from Raspberry Pi from here for under $5: http://www.raspberrypi.com/mpeg-2-license-key/

Update (Audio & Video):

SEE UPDATED CODE BELOW

gst-launch-1.0 -v –gst-debug=*:3 filesrc location=/home/pi/Gotham.2015-01-19.20-00.ts ! decodebin name=demux demux. ! queue ! audioconvert ! “audio/x-raw,format=F32LE” ! avenc_ac3 bitrate=320000 ! mux. avimux name=mux ! filesink location=/media/seagate/Gotham.m4v demux. ! queue ! videoconvert ! omxh264enc ! “video/x-h264,profile=high,cabc=TRUE,bitrate=5000” ! h264parse ! mux.

To be confirmed: (Answered by gst-inspect omxh264enc – added output below)

Is this line working/doing anything???: “video/x-h264,profile=high,cabc=TRUE,bitrate=5000”

gst-inspect omxh264enc output:

Factory Details:
Rank primary + 1 (257)
Long-name OpenMAX H.264 Video Encoder
Klass Codec/Encoder/Video
Description Encode H.264 video streams
Author Sebastian Dröge <sebastian.droege@collabora.co.uk>

Plugin Details:
Name omx
Description GStreamer OpenMAX Plug-ins
Filename /usr/lib/arm-linux-gnueabihf/gstreamer-1.0/libgstomx.so
Version 1.2.0
License LGPL
Source module gst-omx
Source release date 2014-07-23
Binary package GStreamer OpenMAX Plug-ins source release
Origin URL Unknown package origin

GObject
+—-GInitiallyUnowned
+—-GstObject
+—-GstElement
+—-GstVideoEncoder
+—-GstOMXVideoEnc
+—-GstOMXH264Enc
+—-GstOMXH264Enc-omxh264enc

Implemented Interfaces:
GstPreset

Pad Templates:
SINK template: ‘sink’
Availability: Always
Capabilities:
video/x-raw
width: [ 1, 2147483647 ]
height: [ 1, 2147483647 ]
framerate: [ 0/1, 2147483647/1 ]

SRC template: ‘src’
Availability: Always
Capabilities:
video/x-h264
width: [ 16, 4096 ]
height: [ 16, 4096 ]
Element Flags:
no flags set

Element Implementation:
Has change_state() function: gst_omx_video_enc_change_state

Element has no clocking capabilities.
Element has no indexing capabilities.
Element has no URI handling capabilities.

Pads:
SINK: ‘sink’
Implementation:
Has chainfunc(): gst_video_encoder_chain
Has custom eventfunc(): gst_video_encoder_sink_event
Has custom queryfunc(): gst_video_encoder_sink_query
Has custom iterintlinkfunc(): gst_pad_iterate_internal_links_default
Pad Template: ‘sink’
SRC: ‘src’
Implementation:
Has custom eventfunc(): gst_video_encoder_src_event
Has custom queryfunc(): gst_video_encoder_src_query
Has custom iterintlinkfunc(): gst_pad_iterate_internal_links_default
Pad Template: ‘src’

Element Properties:
name : The name of the object
flags: readable, writable
String. Default: “omxh264enc-omxh264enc0”

parent : The parent of the object
flags: readable, writable
Object of type “GstObject”

control-rate : Bitrate control method
flags: readable, writable, changeable only in NULL or READY state
Enum “GstOMXVideoEncControlRate” Default: -1, “default”
(0): disable – Disable
(1): variable – Variable
(2): constant – Constant
(3): variable-skip-frames – Variable Skip Frames
(4): constant-skip-frames – Constant Skip Frames
(-1): default – Component Default

target-bitrate : Target bitrate (0xffffffff=component default)
flags: readable, writable, changeable in NULL, READY, PAUSED or PLAYING state
Unsigned Integer. Range: 0 – 4294967295 Default: 4294967295

quant-i-frames : Quantization parameter for I-frames (0xffffffff=component default)
flags: readable, writable, changeable only in NULL or READY state
Unsigned Integer. Range: 0 – 4294967295 Default: 4294967295

quant-p-frames : Quantization parameter for P-frames (0xffffffff=component default)
flags: readable, writable, changeable only in NULL or READY state
Unsigned Integer. Range: 0 – 4294967295 Default: 4294967295

quant-b-frames : Quantization parameter for B-frames (0xffffffff=component default)
flags: readable, writable, changeable only in NULL or READY state
Unsigned Integer. Range: 0 – 4294967295 Default: 4294967295

inline-header : Inline SPS/PPS header before IDR
flags: readable, writable, changeable only in NULL or READY state
Boolean. Default: true

periodicty-idr : Periodicity of IDR frames (0xffffffff=component default)
flags: readable, writable, changeable only in NULL or READY state
Unsigned Integer. Range: 0 – 4294967295 Default: 4294967295

interval-intraframes: Interval of coding Intra frames (0xffffffff=component default)
flags: readable, writable, changeable only in NULL or READY state
Unsigned Integer. Range: 0 – 4294967295 Default: 4294967295

Changed made to encoding script due to output above:

gst-launch-1.0 filesrc location=”$WorkPath”/”$FileName” ! decodebin name=demux demux. ! queue ! audioconvert ! “audio/x-raw,format=F32LE” ! avenc_ac3 bitrate=320000 ! mux. avimux name=mux ! filesink location=”$WorkPath”/”$OutputName” demux. ! queue ! videoconvert ! omxh264enc ! “video/x-h264,control-rate=1,target-bitrate=5000” ! h264parse ! mux.

Unconfirmed but this may work:

gst-launch-1.0 filesrc location=/home/pi/Mystery-Hunters.2015-01-31.10-00.ts
! queue
! tsdemux name=demuxer matroskamux name=mux
! queue
! filesink location=/home/pi/mystery.mkv demuxer.
! mpegvideoparse
! queue
! omxmpeg2videodec
! videoconvert
! omxh264enc
! “video/x-h264,control-rate=1,target-bitrate=3000,quant-i-frames=100”
! h264parse
! mux. demuxer.
! queue
! avdec_ac3
! audioconvert
! lamemp3enc
! mux.

My Latest Update 02-06-2015 (Seeking help here: http://www.raspberrypi.org/forums/viewtopic.php?f=31&t=98949):

#remove audio track from source and mux into mp4 container
/usr/local/bin/ffmpeg -i “$input” -vn -acodec copy -y “$WorkPath”/temp.m4a

#Transcode Video stream only to mp4 container
gst-launch-1.0 -e filesrc location=”$input”
! tsdemux
! mpegvideoparse
! omxmpeg2videodec
! deinterlace
! videoconvert
! omxh264enc inline-header=true periodicty-idr=1
! “video/x-h264,stream-format=byte-stream,profile=high,control-rate=variable,target-bitrate=5000000,quant-i-frames=250″
! h264parse
! mp4mux ! filesink location=”$WorkPath”/”temp.m4v”

#Mux Audio & Video Files
/usr/local/bin/ffmpeg -i “$WorkPath”/”temp.m4v” -i “$WorkPath”/”temp.m4a” -vcodec copy -map 0:0 -acodec copy -map 1:0 -shortest -y “$OutputPath”/”$OutputName”

Update as of 02-23-2015

I can build gstreamer pipes to work for one file or another file but building one to rule them all is becoming quite a task. Perhaps I should gather smaller test files for faster results instead of full TV shows. I may have to put something in the batch script to detect properties of the transport stream before giving it a pipe. Hopefully it doesn’t come to that.

The script from here: “OnePi Two Pi” for builing Gstreamer 1.2 was instrumental in some audio/video sync issues. That has helped a lot! (Long time to build though. ToDo: Setup cross-compiler).

On a slightly unrelated note I’ve been getting bad Guide data from MC2XML so I’ve switched to Zap2XML and so far I’m not looking back. This Guide info is much better (so far).

Update as of 02-24-2015 (Seems to be working!)

The answer to my problems seems to be to just mux the streams back into a Transport Stream with an h264 video stream. Code I’m using is as follows with another post to explain everything coming when I have time:

/usr/local/bin/gst-launch-1.0 -e filesrc location=”$input”
! decodebin name=demux
! queue
! audioconvert
! audio/x-raw
! audiorate
! avenc_ac3 bitrate=320000
! mux. mpegtsmux name=mux
! filesink location=”$OutputPath”/”$OutputName” demux.
! queue
! videoconvert
! deinterlace
! omxh264enc target-bitrate=2000000 control-rate=1 inline-header=true periodicty-idr=250 interval-intraframes=250
! “video/x-h264,profile=high”
! h264parse
! mux.

Resources I’m looking at for this:

http://www.onepitwopi.com/raspberry-pi/gstreamer-1-2-on-the-raspberry-pi/ *Potentially AWESOME!* -Added 02-19-2015

http://stackoverflow.com/questions/22432566/mpeg2-to-h264-wtih-gstreamer

http://www.raspberrypi.org/forums/viewtopic.php?f=38&t=6852&start=75

http://theiopage.blogspot.com/2013/04/enabling-hardware-h264-encoding-with.html

http://helloraspberrypi.blogspot.com/2014/06/install-gcc-47-on-raspberry-pi-and-set.html

https://solarianprogrammer.com/2015/01/13/raspberry-pi-raspbian-install-gcc-compile-cpp-14-programs/

http://kacianka.at/?p=145

http://docs.gstreamer.com/display/GstSDK/Frequently+Asked+Questions

13 thoughts on “Hardware Video Encoding progess with the Raspberry Pi

  1. Hi, I’m trying to do the exact same thing. Take TVHeadend .ts mpeg2 recordings and transcode them into smaller files. I’m happy to leave the audio as it is and just copy it across to the new container. I don’t care what container, as I play it on XBMC.

    I have come up with a script very similar to yours. But thought I’d copy your script as you say it work, but am getting errors with the variables $input and $output. It seems to work ok if I put file names in but that’s not ideal. Eventually I’d like to create a script that reads a directory, takes the file names and runs then through a transcode script. Then I can cron that daily and skip mp4 or mkv file extensions (as they will have been transcoded previously).

    I’ve also found that TV streams don’t seem to end transcoding too well, I would suspect it is because the stream is ended suddenly. Perhapsy a copy to another container with ffmpeg would clean that up.

    Any tips would be great. I’m not the best linux/scripting person, but have figured it out this far so can follow instruction easily enough, or explore suggestions

    P.S. Nice name 😉

    1. @Andrew I’m sorry for the delay in response. I appreciate your enthusiasm I hope you’ve got further on this since April. My best suggestion on the $input $output is to check Stack Overflow for information on passing variables into bash scripts. I didn’t get too far on pre-processing recordings in a reliable way to make a better video file. My best quick solution for a playable file in Plex (my end goal) was to use the MKV container in gstreamer which seemed to be more forgiving and better at keeping audio and video in sync.

      In a nutshell passing variables into a bash script looks like this:

      Execute the script like this passing in var1 & var2 (could be input & output locations). Separate variables by spaces.

      ./myscript.sh var1 var2

      In the bash script using those variables looks like this:
      #!/bin/bash
      variable1=$1
      variable2=$2

      See this for more: http://stackoverflow.com/questions/2645636/how-to-pass-parameters-to-a-linux-bash-script

  2. Hi,
    I am trying to capture from a v4l device and stream in real time. It would be a cheap alternative to slingbox to stream video content between home televisions.

    1. @Francesco If you’re still interested in this type of thing see my reply to Geo below and sorry for the delay (I don’t get on here often enough).

  3. I am surprised that there aren’t more people trying to use a raspbery pi 2 to transcode from MPEG2 to h264, given it’s low cost and ability to do both operations in hardware. Your last edit indicates you may be creating a fresh post with just the details one would need to pull this off and the latest state of affairs. Are you still planning on doing this?

    I’m super interested in this. I don’t yet have a rasp pi 2 so I haven’t read through everything. Just scanning it over, this looks like a multi-weekend trap. I was hoping that some brave, intelligent soul like your self might have already cleared the way for normals like myself 🙂

    1. @Michael I would really love to get back into this project soon and write up a good tutorial-style post rather than a bunch of ramblings like the one above. Unfortunately my PVR server had a bunch of hard drives crash around the time of this post and I gave up on transcoding DVR recordings for a while. The server is back up (for Plex among other things) but I’m spent with trying to create my own DVR/PVR solution for now.

      I have thought about creating a Raspberry Pi webcam solution with my un-used PS3 Eye camera and I have got it working but noticed the CPU usage was high so that would be a great use-case for hardware video encoding if I get time.

  4. Does this have enough grunt to do realtime ~720p MPEG2 to H264? I don’t mind if there’s a bit of buffering, just that my wifi isn’t good enough to handle MPEG2 TS over the network.

    1. I imagine the difference is my source file was 720P MPEG-2 encoded and their source file was 1080P. Also if the encoding of their source was also h264 it would also be more complex to decode than MPEG-2 (even though both should happen in hardware if you purchase the MPEG 2 Hardware decoder from Raspberry Pi, which I have). They also implemented a processor in the scaling method to improve quality and a speed cost.

    1. I would imagine the answer in both cases is yes. What I would do, if I had the time to work on it now, is I would compile the latest gstreamer from source (takes a few hours on the raspberry pi) See this: http://www.onepitwopi.com/raspberry-pi/gstreamer-1-2-on-the-raspberry-pi/

      After that finished I would play with gstreamer’s live streaming pipe line and probably try to connect to it from another machine on the network with VLC. Here’s some examples of a gstreamer pipeline connecting to a Video 4 Linux device (like a webcam): http://stackoverflow.com/questions/7669240/webcam-streaming-using-gstreamer-over-udp

      You would just want to replace any decoder/encoder in that pipeline with the ones I’ve used in my examples above and if your video 4 linux device outputs mpeg-2 you may want to buy the mpeg-2 hardware decoder from Raspberry Pi.

Leave a Reply