There's always time to play

Wednesday, June 18, 2008

gstreamer television recording

I've been trying to record stuff from my tv tuner card using gstreamer. I like gstreamer as a multimedia framework and I like some apps that use it, so although there are enough other options I wanted to try this using gstreamer first.

The first issue was to record something from the tv card. I started by installing the video4linux gstreamer plugin. I actually started with installing version 1, then noticed there was a version 2 and because that's what the kernel uses natively I decided to go for v4l2. The gstreamer plugin is called v4l2src. I already read that to display something you need to run it through ffmpegcolorspace. To get it more useful you also need to specify resolution and framerate so to get something useful on my screen I started with
gst-launch-0.10 v4l2src device=/dev/video0 ! \
video/x-raw-yuv,width=768,height=520,framerate=\(fraction\)30000/1001 ! \
ffmpegcolorspace ! ximagesink

This works, but you still need to control the tv card or else you'll probably just see snow. There's a few different ways to control the tv card, but I wanted something as small as possible because I only needed to change the frequency to a desired frequency. I found dov4l, a small program that can do all the basic stuff that I needed done. To switch to discovery channel I could do a simple
./dov4l -f 496000000
from the folder where I unpacked the dov4l sources after I ran make. So now I actually had discovery channel displaying on my computer screen. Next step was audio. I use a bt878 tv card so I can use snd_bt87x to capture audio from the tuner device. A simple
gst-launch-0.10 alsasrc device=hw:1,0 ! alsasink
gave sound, so now I had audio and video from my tv card.

Now the hardest part was to get it all into a file. I'd seen various websites explaining some simple gstreamer stuff, and that at least taught me about muxing and demuxing. Actually, I didn't know much about the basics either. In short you usually want to encode video into something, encode audio into something and then dump it into a media container. So one common media container everyone knows is the avi format. I actually started out with ogg (vorbis and theora in an ogg container), but that gave me issues with both picture and sound so I soon moved to lame to convert my audio into mp3 and used ffenc_mpeg4 to convert my video into mpeg4. Dumping that into an avi container I ran into audio sync issues. After asking in #gstreamer on freenode I was told that the avi container doesn't handle time very well (or at all), so I should use another container and they pointed me to matroska. At this moment I was able to record to a file and view it afterwards, but that was not enough for me...

I wanted to use something modern. Although I quite like matroska containers because they're quite well supported on all platforms (vlc supports them, so any platform that supports vlc can in some way handle them) I was looking more for something like mp4. ffmpeg offers a muxing plugin for mp4, but I haven't gotten it to also use audio and I can only play it with mplayer then, so that's not really an option. I could however use something else in the matroska container. So I decided to use x264 for video. I'm on gentoo so I prefer to install by using ebuilds (although I'm using paludis so importare works for me too), so I went looking for an ebuild. The easy solution is to copy an existing gst-plugins-bad plugin ebuild (like the lame ebuild) and name it gst-plugins-x264-0.10.6.ebuild and place it in a correctly named folder in an overlay. Remove the deps that don't make sense (only lame for this specific plugin) and you should be able to install the plugin. I first tried with -0.10.7, but that wants gstreamer-0.10.19, so I decided to stick with gst-plugins-x264-0.10.6. Now I also needed something decent for audio, and I decided to go for faac, which seems to be reasonably supported these days and I ended up with the following line to do nice gstreamer recording from my tv card:
gst-launch-0.10 matroskamux name=mux ! \
filesink location=test.mkv alsasrc device=hw:1,0 ! \
audio/x-raw-int,channels=2,rate=32000,depth=16 ! \
audioconvert ! faac ! queue ! \
mux. v4l2src device=/dev/video0 ! \
video/x-raw-yuv,width=768,height=520,framerate=\(fraction\)30000/1001 ! \
ffmpegcolorspace ! x264enc ! queue ! mux.

I started with a capture resolution of 924x576, because that's what my card supports, but x264enc doesn't like that. Besides the fact that it complains that it can't encode fast when the width or height is not a multiple of 16 it wasn't properly encoding on that resolution. So as you can see I dropped it to 768x520, which gives a very decent picture, even for full screen viewing. Have fun watching!


Sam said...

Please, could you tell me what tv card you used?
I'm trying something like that with a Hauppauge 1300 and it doesn't work.


Sam said...

Please, could you tell me what tv card you used?
I'm trying something like that with a Hauppauge 1300 and it doesn't work.


Michael Croes said...

I'm using an older Hauppauge card, a winTV model, don't know exactly which one. The issue in your case most likely is that you have a hardware mpeg encoder, so you can't use the gstreamer v4l plugin to watch it. It might however be possible that you can just use an mpeg demuxer to watch your stream.

Sam said...

Thanks for the reply.
I'm trying to capture the composite video input directly, but gstreamer always answers me "could not negotiate format..."

Unknown said...

Hey Micheal,

I just read your blog. I have come across some problem and i wonder if you can help me. I was trying to steam a video from a file(.mp4 H264 format, 320x240) to a full screen of 800x600. But it got stuck and the screen freezes. Here is what i used.

gst-launch filesrc -v location=/video/test.mp4 ! mfw_mp4demuxer name=demux demux. ! mfw_vpudecoder codec-type=std_avc ! mfw_v4lsink disp-width=800 disp-height=600 demux. &

and here is what i get...
mx27# Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
/pipeline0/mfwgstvpu_dec0.sink: caps = video/x-h264, mpegversion=(int)4, systemstream=(int)0, height=(int)240, width=(int)320, framerate=(fraction)25/1
/pipeline0/mfwgstvpu_dec0.sink: caps = video/x-h264, mpegversion=(int)4, systemstream=(int)0, height=(int)240, width=(int)320, framerate=(fraction)25/1
/pipeline0/mfwgstvpu_dec0.src: caps = video/x-raw-yuv, format=(fourcc)I420, width=(int)320, height=(int)240, pixel-aspect-ratio=(fraction)1/1, num-buffers-required=(int)4
/pipeline0/mfw_gst_v4lsink_info_t0.sink: caps = video/x-raw-yuv, format=(fourcc)I420, width=(int)320, height=(int)240, pixel-aspect-ratio=(fraction)1/1, num-buffers-required=(int)4
WARNING: from element /pipeline0/demux: pads are not negotiated!
Additional debug info:
audio pads are not negotiated!
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock

and the screen freezes. What is the problem in full screen? Because it plays fine with the original resolution, 320x240.



Michael Croes said...

The only thing I can think of is that the resolution is the issue, because it's not a multitude of 16 pixels in height.