diff options
author | jcorgan | 2006-08-03 04:51:51 +0000 |
---|---|---|
committer | jcorgan | 2006-08-03 04:51:51 +0000 |
commit | 5d69a524f81f234b3fbc41d49ba18d6f6886baba (patch) | |
tree | b71312bf7f1e8d10fef0f3ac6f28784065e73e72 /gr-video-sdl/src | |
download | gnuradio-5d69a524f81f234b3fbc41d49ba18d6f6886baba.tar.gz gnuradio-5d69a524f81f234b3fbc41d49ba18d6f6886baba.tar.bz2 gnuradio-5d69a524f81f234b3fbc41d49ba18d6f6886baba.zip |
Houston, we have a trunk.
git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@3122 221aa14e-8319-0410-a670-987f0aec2ac5
Diffstat (limited to 'gr-video-sdl/src')
-rw-r--r-- | gr-video-sdl/src/Makefile.am | 86 | ||||
-rwxr-xr-x | gr-video-sdl/src/qa_video_sdl.py | 40 | ||||
-rw-r--r-- | gr-video-sdl/src/run_tests.in | 47 | ||||
-rw-r--r-- | gr-video-sdl/src/video_sdl.i | 67 | ||||
-rw-r--r-- | gr-video-sdl/src/video_sdl_sink_s.cc | 301 | ||||
-rw-r--r-- | gr-video-sdl/src/video_sdl_sink_s.h | 90 | ||||
-rw-r--r-- | gr-video-sdl/src/video_sdl_sink_uc.cc | 291 | ||||
-rw-r--r-- | gr-video-sdl/src/video_sdl_sink_uc.h | 90 |
8 files changed, 1012 insertions, 0 deletions
diff --git a/gr-video-sdl/src/Makefile.am b/gr-video-sdl/src/Makefile.am new file mode 100644 index 000000000..0a28e0cef --- /dev/null +++ b/gr-video-sdl/src/Makefile.am @@ -0,0 +1,86 @@ +# +# Copyright 2004,2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +include $(top_srcdir)/Makefile.common + +# Install this stuff so that it ends up as the gnuradio.video_sdl module +# This usually ends up at: +# ${prefix}/lib/python${python_version}/site-packages/gnuradio + +ourpythondir = $(grpythondir) +ourlibdir = $(grpyexecdir) + +EXTRA_DIST = run_tests.in +TESTS = run_tests + +LOCAL_IFILES = \ + video_sdl.i + +NON_LOCAL_IFILES = \ + $(top_srcdir)/gnuradio-core/src/lib/swig/gnuradio.i + +ALL_IFILES = \ + $(LOCAL_IFILES) \ + $(NON_LOCAL_IFILES) + +BUILT_SOURCES = \ + video_sdl.cc \ + video_sdl.py + +ourpython_PYTHON = \ + video_sdl.py + +INCLUDES = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) $(SDL_CFLAGS) + +SWIGCPPPYTHONARGS = -fvirtual -python -modern $(PYTHON_CPPFLAGS) $(STD_DEFINES_AND_INCLUDES) + +ourlib_LTLIBRARIES = _video_sdl.la + +_video_sdl_la_SOURCES = \ + video_sdl.cc \ + video_sdl_sink_uc.cc \ + video_sdl_sink_s.cc + + +grinclude_HEADERS = \ + video_sdl_sink_uc.h \ + video_sdl_sink_s.h + +swiginclude_HEADERS = \ + $(LOCAL_IFILES) + +_video_sdl_la_LIBADD = \ + $(PYTHON_LDFLAGS) \ + $(GNURADIO_CORE_LIBS) \ + $(SDL_LIBS) \ + -lstdc++ + +_video_sdl_la_LDFLAGS = $(NO_UNDEFINED) -module -avoid-version + +video_sdl.cc video_sdl.py: video_sdl.i + $(SWIG) $(SWIGCPPPYTHONARGS) -module video_sdl -o video_sdl.cc $< + + +noinst_PYTHON = qa_video_sdl.py + +MOSTLYCLEANFILES = \ + $(BUILT_SOURCES) *~ *.pyc + diff --git a/gr-video-sdl/src/qa_video_sdl.py b/gr-video-sdl/src/qa_video_sdl.py new file mode 100755 index 000000000..905b28715 --- /dev/null +++ b/gr-video-sdl/src/qa_video_sdl.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# +# Copyright 2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import video_sdl + +class qa_video_sdl (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_000_nop (self): + """Just see if we can import the module... + They may not have video drivers, etc. Don't try to run anything""" + pass + +if __name__ == '__main__': + gr_unittest.main () diff --git a/gr-video-sdl/src/run_tests.in b/gr-video-sdl/src/run_tests.in new file mode 100644 index 000000000..2f84c3786 --- /dev/null +++ b/gr-video-sdl/src/run_tests.in @@ -0,0 +1,47 @@ +#!/bin/sh + +# All this strange PYTHONPATH manipulation is required to run our +# tests using our just built shared library and swig-generated python +# code prior to installation. + +# build tree == src tree unless you're doing a VPATH build. +# If you don't know what a VPATH build is, you're not doing one. Relax... + +prefix=@prefix@ +exec_prefix=@exec_prefix@ + +# Where to look in the build tree for our shared library +libbld=@abs_top_builddir@/gr-video-sdl/src +# Where to look in the src tree for swig generated python code +libsrc=@abs_top_srcdir@/gr-video-sdl/src +# Where to look in the src tree for hand written python code +py=@abs_top_srcdir@/gr-video-sdl/src + +# Where to look for GNU Radio python modules in current build tree +# FIXME this is wrong on a distcheck. We really need to ask gnuradio-core +# where it put its python files. +grpythonbld=@abs_top_builddir@/gnuradio-core/src/python/:@abs_top_builddir@/gnuradio-core/src/lib/swig/:@abs_top_builddir@/gnuradio-core/src/lib/swig/.libs + +PYTHONPATH="$grpythonbld:$libbld:$libbld/.libs:$libsrc:$py:$PYTHONPATH" +export PYTHONPATH + +# +# This is the simple part... +# Run everything that matches qa_*.py and return the final result. +# + +ok=yes +for file in @srcdir@/qa_*.py +do + if ! $file + then + ok=no + fi +done + +if [ $ok = yes ] +then + exit 0 +else + exit 1 +fi diff --git a/gr-video-sdl/src/video_sdl.i b/gr-video-sdl/src/video_sdl.i new file mode 100644 index 000000000..e70d12356 --- /dev/null +++ b/gr-video-sdl/src/video_sdl.i @@ -0,0 +1,67 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +%feature("autodoc","1"); + +%include "exception.i" +%import "gnuradio.i" // the common stuff + +%{ +#include "gnuradio_swig_bug_workaround.h" // mandatory bug fix +#include "video_sdl_sink_uc.h" +#include "video_sdl_sink_s.h" +#include <stdexcept> +%} + +// ---------------------------------------------------------------- + +GR_SWIG_BLOCK_MAGIC(video_sdl,sink_uc) + +video_sdl_sink_uc_sptr +video_sdl_make_sink_uc ( double framerate,int width=640, int height=480,unsigned int format=0,int dst_width=-1,int dst_height=-1 + ) throw (std::runtime_error); + + +class video_sdl_sink_uc : public gr_sync_block { + protected: + video_sdl_sink_uc (double framerate,int width, int height,gr_uint32 format,int dst_width,int dst_height); + + public: + ~video_sdl_sink_uc (); +}; + +// ---------------------------------------------------------------- + +GR_SWIG_BLOCK_MAGIC(video_sdl,sink_s) + +video_sdl_sink_s_sptr +video_sdl_make_sink_s ( double framerate,int width=640, int height=480,unsigned int format=0,int dst_width=-1,int dst_height=-1 + ) throw (std::runtime_error); + + +class video_sdl_sink_s : public gr_sync_block { + protected: + video_sdl_sink_s (double framerate,int width, int height,gr_uint32 format,int dst_width,int dst_height); + + public: + ~video_sdl_sink_s (); +}; diff --git a/gr-video-sdl/src/video_sdl_sink_s.cc b/gr-video-sdl/src/video_sdl_sink_s.cc new file mode 100644 index 000000000..50e730dbc --- /dev/null +++ b/gr-video-sdl/src/video_sdl_sink_s.cc @@ -0,0 +1,301 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <SDL.h> + +#include <video_sdl_sink_s.h> +#include <gr_io_signature.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <iostream> +#include <stdexcept> + + + +video_sdl_sink_s::video_sdl_sink_s (double framerate,int width, int height,unsigned int format,int dst_width,int dst_height) + : gr_sync_block ("video_sdl_sink_s", + gr_make_io_signature (1, 3, sizeof (short)), + gr_make_io_signature (0, 0, 0)), + d_chunk_size (width*height), + d_framerate(framerate), + d_wanted_frametime_ms(0), + d_width(width), + d_height (height), + d_dst_width(dst_width), + d_dst_height(dst_height), + d_format(format), + d_current_line(0), + d_screen(NULL), + d_image(NULL), + d_avg_delay(0.0), + d_wanted_ticks(0) +{ + if(framerate<=0.0) + d_wanted_frametime_ms=0;//Go as fast as possible + else + d_wanted_frametime_ms=(int)(1000.0/framerate); + if(dst_width<0) d_dst_width=d_width; + if(dst_height<0) d_dst_height=d_height; + if(0==format) d_format=IMGFMT_YV12; + + atexit(SDL_Quit);//check if this is the way to do this + if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) { + std::cerr << "video_sdl_sink_s: Couldn't initialize SDL:" << SDL_GetError() << " \n SDL_Init(SDL_INIT_VIDEO) failed\n"; + throw std::runtime_error ("video_sdl_sink_s"); + }; + + /* accept any depth */ + d_screen = SDL_SetVideoMode(dst_width, dst_height, 0, SDL_SWSURFACE|SDL_RESIZABLE|SDL_ANYFORMAT);//SDL_DOUBLEBUF |SDL_SWSURFACE| SDL_HWSURFACE||SDL_FULLSCREEN + if ( d_screen == NULL ) { + std::cerr << "Unable to set SDL video mode: " << SDL_GetError() <<"\n SDL_SetVideoMode() Failed \n"; + exit(1); + } + if ( d_image ) { + SDL_FreeYUVOverlay(d_image); + } + /* Initialize and create the YUV Overlay used for video out */ + if (!(d_image = SDL_CreateYUVOverlay (d_width, d_height, SDL_YV12_OVERLAY, d_screen))) { + std::cerr << "SDL: Couldn't create a YUV overlay: \n"<< SDL_GetError() <<"\n"; + throw std::runtime_error ("video_sdl_sink_s"); + } + + printf("SDL screen_mode %d bits-per-pixel\n", + d_screen->format->BitsPerPixel); + printf("SDL overlay_mode %i \n", + d_image->format); + d_chunk_size = std::min(1,16384/width); //width*16; + d_chunk_size = d_chunk_size*width; + //d_chunk_size = (int) (width); + set_output_multiple (d_chunk_size); + /* Set the default playback area */ + d_dst_rect.x = 0; + d_dst_rect.y = 0; + d_dst_rect.w = d_dst_width; + d_dst_rect.h = d_dst_height; + //clear the surface to grey + if ( SDL_LockYUVOverlay( d_image ) ) { + std::cerr << "SDL: Couldn't lock YUV overlay: \n"<< SDL_GetError() <<"\n"; + throw std::runtime_error ("video_sdl_sink_s"); + } + memset(d_image->pixels[0], 128, d_image->pitches[0]*d_height); + memset(d_image->pixels[1], 128, d_image->pitches[1]*d_height/2); + memset(d_image->pixels[2], 128, d_image->pitches[2]*d_height/2); + SDL_UnlockYUVOverlay( d_image ); +} + +video_sdl_sink_s::~video_sdl_sink_s () +{ + SDL_Quit(); +} + +video_sdl_sink_s_sptr +video_sdl_make_sink_s (double framerate,int width, int height,unsigned int format,int dst_width,int dst_height) +{ + return video_sdl_sink_s_sptr (new video_sdl_sink_s (framerate, width, height,format,dst_width,dst_height)); +} + +void +video_sdl_sink_s::copy_line_pixel_interleaved(unsigned char *dst_pixels_u,unsigned char *dst_pixels_v,const short * src_pixels,int src_width) +{ + for(int i=0;i<src_width;i++) + { + dst_pixels_u[i]=(unsigned char)src_pixels[i*2]; + dst_pixels_v[i]=(unsigned char)src_pixels[i*2+1]; + } + return; +} + +void +video_sdl_sink_s::copy_line_line_interleaved(unsigned char *dst_pixels_u,unsigned char *dst_pixels_v,const short * src_pixels,int src_width) +{ + for(int i=0;i<src_width;i++) + { + dst_pixels_u[i]=(unsigned char)src_pixels[i]; + dst_pixels_v[i]=(unsigned char)src_pixels[i+src_width]; + } + for(int i=src_width;i<src_width*2;i++) + { + dst_pixels_v[i]=(unsigned char)src_pixels[i]; + } + return; +} +void +video_sdl_sink_s::copy_line_single_plane(unsigned char *dst_pixels,const short * src_pixels,int src_width) +{ + for(int i=0;i<src_width;i++) + { + dst_pixels[i]=(unsigned char)src_pixels[i]; + } + return; +} + +void +video_sdl_sink_s::copy_line_single_plane_dec2(unsigned char *dst_pixels,const short * src_pixels,int src_width) +{ + for(int i=0,j=0;i<src_width;i+=2,j++) + { + dst_pixels[j]=(unsigned char)src_pixels[i]; + } + return; +} + +int +video_sdl_sink_s::copy_plane_to_surface (int plane,int noutput_items, + const short * src_pixels) +{ + const int first_dst_plane=(12==plane ||1122==plane)?1:plane; + const int second_dst_plane=(12==plane ||1122==plane)?2:plane; + int current_line=(0==plane)?d_current_line:d_current_line/2; + unsigned char * dst_pixels = (unsigned char *)d_image->pixels[first_dst_plane]; + dst_pixels=&dst_pixels[current_line*d_image->pitches[first_dst_plane]]; + unsigned char * dst_pixels_2 = (unsigned char *)d_image->pixels[second_dst_plane]; + dst_pixels_2=&dst_pixels_2[current_line*d_image->pitches[second_dst_plane]]; + int src_width=(0==plane || 12==plane || 1122==plane)?d_width:d_width/2; + int noutput_items_produced=0; + int max_height=(0==plane)?d_height-1:d_height/2-1; + for (int i = 0; i < noutput_items; i += src_width){ + //output one line at a time + if(12==plane) + { + copy_line_pixel_interleaved(dst_pixels,dst_pixels_2,src_pixels,src_width); + dst_pixels_2 += d_image->pitches[second_dst_plane]; + } + else if (1122==plane) + { + copy_line_line_interleaved(dst_pixels,dst_pixels_2,src_pixels,src_width); + dst_pixels_2 += d_image->pitches[second_dst_plane]; + src_pixels += src_width; + } + else if (0==plane) + copy_line_single_plane(dst_pixels,src_pixels,src_width); + else /* 1==plane || 2==plane*/ + copy_line_single_plane_dec2(dst_pixels,src_pixels,src_width);//decimate by two horizontally + + src_pixels += src_width; + dst_pixels += d_image->pitches[first_dst_plane]; + noutput_items_produced+=src_width; + current_line++; + if (current_line>max_height) + { + //Start new frame + //TODO, do this all in a seperate thread + current_line=0; + dst_pixels=d_image->pixels[first_dst_plane]; + dst_pixels_2=d_image->pixels[second_dst_plane]; + if(0==plane) + { + SDL_DisplayYUVOverlay(d_image, &d_dst_rect); + //SDL_Flip(d_screen); + unsigned int ticks=SDL_GetTicks();//milliseconds + d_wanted_ticks+=d_wanted_frametime_ms; + float avg_alpha=0.1; + int time_diff=d_wanted_ticks-ticks; + d_avg_delay=time_diff*avg_alpha +d_avg_delay*(1.0-avg_alpha); + } + } + } + if(0==plane) d_current_line=current_line; + return noutput_items_produced; +} + +int +video_sdl_sink_s::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + short *src_pixels_0,*src_pixels_1,*src_pixels_2; + int noutput_items_produced=0; + int plane; + int delay=(int)d_avg_delay; + if(0==d_wanted_ticks) + d_wanted_ticks=SDL_GetTicks(); + if(delay>0) + SDL_Delay((unsigned int)delay);//compensate if running too fast + + if ( SDL_LockYUVOverlay( d_image ) ) { + return 0; + } + switch (input_items.size ()){ + case 3: // first channel=Y, second channel is U , third channel is V + src_pixels_0 = (short *) input_items[0]; + src_pixels_1 = (short *) input_items[1]; + src_pixels_2 = (short *) input_items[2]; + for (int i = 0; i < noutput_items; i += d_chunk_size){ + copy_plane_to_surface (1,d_chunk_size, src_pixels_1); + copy_plane_to_surface (2,d_chunk_size, src_pixels_2); + noutput_items_produced+=copy_plane_to_surface (0,d_chunk_size, src_pixels_0); + src_pixels_0 += d_chunk_size; + src_pixels_1 += d_chunk_size; + src_pixels_2 += d_chunk_size; + } + break; + case 2: + if(1) //if(pixel_interleaved_uv) + { + // first channel=Y, second channel is alternating pixels U and V + src_pixels_0 = (short *) input_items[0]; + src_pixels_1 = (short *) input_items[1]; + for (int i = 0; i < noutput_items; i += d_chunk_size){ + copy_plane_to_surface (12,d_chunk_size/2, src_pixels_1); + noutput_items_produced+=copy_plane_to_surface (0,d_chunk_size, src_pixels_0); + src_pixels_0 += d_chunk_size; + src_pixels_1 += d_chunk_size; + } + } else + { + // first channel=Y, second channel is alternating lines U and V + src_pixels_0 = (short *) input_items[0]; + src_pixels_1 = (short *) input_items[1]; + for (int i = 0; i < noutput_items; i += d_chunk_size){ + copy_plane_to_surface (1222,d_chunk_size/2, src_pixels_1); + noutput_items_produced+=copy_plane_to_surface (0,d_chunk_size, src_pixels_0); + src_pixels_0 += d_chunk_size; + src_pixels_1 += d_chunk_size; + } + } + break; + case 1: // grey (Y) input + /* Y component */ + plane=0; + src_pixels_0 = (short *) input_items[plane]; + for (int i = 0; i < noutput_items; i += d_chunk_size){ + noutput_items_produced+=copy_plane_to_surface (plane,d_chunk_size, src_pixels_0); + src_pixels_0 += d_chunk_size; + } + break; + default: //0 or more then 3 channels + std::cerr << "video_sdl_sink_s: Wrong number of channels: "; + std::cerr <<"1, 2 or 3 channels are supported.\n Requested number of channels is "<< input_items.size () <<"\n"; + throw std::runtime_error ("video_sdl_sink_s"); + } + + SDL_UnlockYUVOverlay( d_image ); + return noutput_items_produced; +} diff --git a/gr-video-sdl/src/video_sdl_sink_s.h b/gr-video-sdl/src/video_sdl_sink_s.h new file mode 100644 index 000000000..662c52b93 --- /dev/null +++ b/gr-video-sdl/src/video_sdl_sink_s.h @@ -0,0 +1,90 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef INCLUDED_VIDEO_SDL_SINK_S_H +#define INCLUDED_VIDEO_SDL_SINK_S_H + +#include <gr_sync_block.h> +#include <string> +#include <SDL.h> + +/* fourcc (four character code) */ +#define vid_fourcc(a,b,c,d) (((unsigned)(a)<<0) | ((unsigned)(b)<<8) | ((unsigned)(c)<<16) | ((unsigned)(d)<<24)) +#define IMGFMT_YV12 vid_fourcc('Y','V','1','2') /* 12 YVU 4:2:0 */ + +class video_sdl_sink_s; +typedef boost::shared_ptr<video_sdl_sink_s> video_sdl_sink_s_sptr; + +video_sdl_sink_s_sptr +video_sdl_make_sink_s (double framerate,int width=640, int height=480,unsigned int format=IMGFMT_YV12,int dst_width=-1,int dst_height=-1); + +/*! + * \brief video sink using SDL + * + * input signature is one, two or three streams of signed short. + * One stream: stream is grey (Y) + * two streems: first is grey (Y), second is alternating U and V + * Three streams: first is grey (Y), second is U, third is V + * Input samples must be in the range [0,255]. + */ + +class video_sdl_sink_s : public gr_sync_block { + friend video_sdl_sink_s_sptr + video_sdl_make_sink_s (double framerate,int width, int height,unsigned int format,int dst_width,int dst_height); + + int d_chunk_size; + + protected: + video_sdl_sink_s (double framerate,int width, int height,unsigned int format, + int dst_width,int dst_height); + void copy_line_pixel_interleaved(unsigned char *dst_pixels_u,unsigned char *dst_pixels_v, + const short * src_pixels,int src_width); + void copy_line_line_interleaved(unsigned char *dst_pixels_u,unsigned char *dst_pixels_v, + const short * src_pixels,int src_width); + void copy_line_single_plane(unsigned char *dst_pixels,const short * src_pixels,int src_width); + void copy_line_single_plane_dec2(unsigned char *dst_pixels,const short * src_pixels,int src_width); + int copy_plane_to_surface (int plane,int noutput_items, + const short * src_pixels); + float d_framerate; + int d_wanted_frametime_ms; + int d_width; + int d_height; + int d_dst_width; + int d_dst_height; + int d_format; + int d_current_line; + SDL_Surface *d_screen; + SDL_Overlay *d_image; + SDL_Rect d_dst_rect; + float d_avg_delay; + unsigned int d_wanted_ticks; + + + public: + ~video_sdl_sink_s (); + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif /* INCLUDED_VIDEO_SDL_SINK_S_H */ diff --git a/gr-video-sdl/src/video_sdl_sink_uc.cc b/gr-video-sdl/src/video_sdl_sink_uc.cc new file mode 100644 index 000000000..1462d7287 --- /dev/null +++ b/gr-video-sdl/src/video_sdl_sink_uc.cc @@ -0,0 +1,291 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <SDL.h> + +#include <video_sdl_sink_uc.h> +#include <gr_io_signature.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <iostream> +#include <stdexcept> + + + +video_sdl_sink_uc::video_sdl_sink_uc (double framerate,int width, int height,unsigned int format,int dst_width,int dst_height) + : gr_sync_block ("video_sdl_sink_uc", + gr_make_io_signature (1, 3, sizeof (unsigned char)), + gr_make_io_signature (0, 0, 0)), + d_chunk_size (width*height), + d_framerate(framerate), + d_wanted_frametime_ms(0), + d_width(width), + d_height (height), + d_dst_width(dst_width), + d_dst_height(dst_height), + d_format(format), + d_current_line(0), + d_screen(NULL), + d_image(NULL), + d_avg_delay(0.0), + d_wanted_ticks(0) +{ + if(framerate<=0.0) + d_wanted_frametime_ms=0;//Go as fast as possible + else + d_wanted_frametime_ms=(int)(1000.0/framerate); + if(dst_width<0) d_dst_width=d_width; + if(dst_height<0) d_dst_height=d_height; + if(0==format) d_format=IMGFMT_YV12; + + atexit(SDL_Quit);//check if this is the way to do this + if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) { + std::cerr << "video_sdl_sink_uc: Couldn't initialize SDL:" << SDL_GetError() << " \n SDL_Init(SDL_INIT_VIDEO) failed\n"; + throw std::runtime_error ("video_sdl_sink_uc"); + }; + + /* accept any depth */ + d_screen = SDL_SetVideoMode(dst_width, dst_height, 0, SDL_SWSURFACE|SDL_RESIZABLE|SDL_ANYFORMAT);//SDL_DOUBLEBUF |SDL_SWSURFACE| SDL_HWSURFACE||SDL_FULLSCREEN + if ( d_screen == NULL ) { + std::cerr << "Unable to set SDL video mode: " << SDL_GetError() <<"\n SDL_SetVideoMode() Failed \n"; + exit(1); + } + if ( d_image ) { + SDL_FreeYUVOverlay(d_image); + } + /* Initialize and create the YUV Overlay used for video out */ + if (!(d_image = SDL_CreateYUVOverlay (d_width, d_height, SDL_YV12_OVERLAY, d_screen))) { + std::cerr << "SDL: Couldn't create a YUV overlay: \n"<< SDL_GetError() <<"\n"; + throw std::runtime_error ("video_sdl_sink_uc"); + } + + printf("SDL screen_mode %d bits-per-pixel\n", + d_screen->format->BitsPerPixel); + printf("SDL overlay_mode %i \n", + d_image->format); + d_chunk_size = std::min(1,16384/width); //width*16; + d_chunk_size = d_chunk_size*width; + //d_chunk_size = (int) (width); + set_output_multiple (d_chunk_size); + /* Set the default playback area */ + d_dst_rect.x = 0; + d_dst_rect.y = 0; + d_dst_rect.w = d_dst_width; + d_dst_rect.h = d_dst_height; + //clear the surface to grey + if ( SDL_LockYUVOverlay( d_image ) ) { + std::cerr << "SDL: Couldn't lock YUV overlay: \n"<< SDL_GetError() <<"\n"; + throw std::runtime_error ("video_sdl_sink_uc"); + } + memset(d_image->pixels[0], 128, d_image->pitches[0]*d_height); + memset(d_image->pixels[1], 128, d_image->pitches[1]*d_height/2); + memset(d_image->pixels[2], 128, d_image->pitches[2]*d_height/2); + SDL_UnlockYUVOverlay( d_image ); +} + +video_sdl_sink_uc::~video_sdl_sink_uc () +{ + SDL_Quit(); +} + +video_sdl_sink_uc_sptr +video_sdl_make_sink_uc (double framerate,int width, int height,unsigned int format,int dst_width,int dst_height) +{ + return video_sdl_sink_uc_sptr (new video_sdl_sink_uc (framerate, width, height,format,dst_width,dst_height)); +} + +void +video_sdl_sink_uc::copy_line_pixel_interleaved(unsigned char *dst_pixels_u,unsigned char *dst_pixels_v,const unsigned char * src_pixels,int src_width) +{ + for(int i=0;i<src_width;i++) + { + dst_pixels_u[i]=src_pixels[i*2]; + dst_pixels_v[i]=src_pixels[i*2+1]; + } + return; +} + +void +video_sdl_sink_uc::copy_line_line_interleaved(unsigned char *dst_pixels_u,unsigned char *dst_pixels_v,const unsigned char * src_pixels,int src_width) +{ + memcpy(dst_pixels_u, src_pixels, src_width); + memcpy(dst_pixels_v, src_pixels+src_width, src_width); + return; +} + +void +video_sdl_sink_uc::copy_line_single_plane(unsigned char *dst_pixels,const unsigned char * src_pixels,int src_width) +{ + memcpy(dst_pixels, src_pixels, src_width); + return; +} + +void +video_sdl_sink_uc::copy_line_single_plane_dec2(unsigned char *dst_pixels,const unsigned char * src_pixels,int src_width) +{ + for(int i=0,j=0;i<src_width;i+=2,j++) + { + dst_pixels[j]=(unsigned char)src_pixels[i]; + } + return; +} + +int +video_sdl_sink_uc::copy_plane_to_surface (int plane,int noutput_items, + const unsigned char * src_pixels) +{ + const int first_dst_plane=(12==plane ||1122==plane)?1:plane; + const int second_dst_plane=(12==plane ||1122==plane)?2:plane; + int current_line=(0==plane)?d_current_line:d_current_line/2; + unsigned char * dst_pixels = (unsigned char *)d_image->pixels[first_dst_plane]; + dst_pixels=&dst_pixels[current_line*d_image->pitches[first_dst_plane]]; + unsigned char * dst_pixels_2 = (unsigned char *)d_image->pixels[second_dst_plane]; + dst_pixels_2=&dst_pixels_2[current_line*d_image->pitches[second_dst_plane]]; + int src_width=(0==plane || 12==plane || 1122==plane)?d_width:d_width/2; + int noutput_items_produced=0; + int max_height=(0==plane)?d_height-1:d_height/2-1; + for (int i = 0; i < noutput_items; i += src_width){ + //output one line at a time + if(12==plane) + { + copy_line_pixel_interleaved(dst_pixels,dst_pixels_2,src_pixels,src_width); + dst_pixels_2 += d_image->pitches[second_dst_plane]; + } + else if (1122==plane) + { + copy_line_line_interleaved(dst_pixels,dst_pixels_2,src_pixels,src_width); + dst_pixels_2 += d_image->pitches[second_dst_plane]; + src_pixels += src_width; + } + else if (0==plane) + copy_line_single_plane(dst_pixels,src_pixels,src_width); + else /* 1==plane || 2==plane*/ + copy_line_single_plane_dec2(dst_pixels,src_pixels,src_width);//decimate by two horizontally + src_pixels += src_width; + dst_pixels += d_image->pitches[first_dst_plane]; + noutput_items_produced+=src_width; + current_line++; + if (current_line>max_height) + { + //Start new frame + //TODO, do this all in a seperate thread + current_line=0; + dst_pixels=d_image->pixels[first_dst_plane]; + dst_pixels_2=d_image->pixels[second_dst_plane]; + if(0==plane) + { + SDL_DisplayYUVOverlay(d_image, &d_dst_rect); + //SDL_Flip(d_screen); + unsigned int ticks=SDL_GetTicks();//milliseconds + d_wanted_ticks+=d_wanted_frametime_ms; + float avg_alpha=0.1; + int time_diff=d_wanted_ticks-ticks; + d_avg_delay=time_diff*avg_alpha +d_avg_delay*(1.0-avg_alpha); + } + } + } + if(0==plane) d_current_line=current_line; + return noutput_items_produced; +} + +int +video_sdl_sink_uc::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + unsigned char *src_pixels_0,*src_pixels_1,*src_pixels_2; + int noutput_items_produced=0; + int plane; + int delay=(int)d_avg_delay; + if(0==d_wanted_ticks) + d_wanted_ticks=SDL_GetTicks(); + if(delay>0) + SDL_Delay((unsigned int)delay);//compensate if running too fast + + if ( SDL_LockYUVOverlay( d_image ) ) { + return 0; + } + switch (input_items.size ()){ + case 3: // first channel=Y, second channel is U , third channel is V + src_pixels_0 = (unsigned char *) input_items[0]; + src_pixels_1 = (unsigned char *) input_items[1]; + src_pixels_2 = (unsigned char *) input_items[2]; + for (int i = 0; i < noutput_items; i += d_chunk_size){ + copy_plane_to_surface (1,d_chunk_size, src_pixels_1); + copy_plane_to_surface (2,d_chunk_size, src_pixels_2); + noutput_items_produced+=copy_plane_to_surface (0,d_chunk_size, src_pixels_0); + src_pixels_0 += d_chunk_size; + src_pixels_1 += d_chunk_size; + src_pixels_2 += d_chunk_size; + } + break; + case 2: + if(1) //if(pixel_interleaved_uv) + { + // first channel=Y, second channel is alternating pixels U and V + src_pixels_0 = (unsigned char *) input_items[0]; + src_pixels_1 = (unsigned char *) input_items[1]; + for (int i = 0; i < noutput_items; i += d_chunk_size){ + copy_plane_to_surface (12,d_chunk_size/2, src_pixels_1); + noutput_items_produced+=copy_plane_to_surface (0,d_chunk_size, src_pixels_0); + src_pixels_0 += d_chunk_size; + src_pixels_1 += d_chunk_size; + } + } else + { + // first channel=Y, second channel is alternating lines U and V + src_pixels_0 = (unsigned char *) input_items[0]; + src_pixels_1 = (unsigned char *) input_items[1]; + for (int i = 0; i < noutput_items; i += d_chunk_size){ + copy_plane_to_surface (1222,d_chunk_size/2, src_pixels_1); + noutput_items_produced+=copy_plane_to_surface (0,d_chunk_size, src_pixels_0); + src_pixels_0 += d_chunk_size; + src_pixels_1 += d_chunk_size; + } + } + break; + case 1: // grey (Y) input + /* Y component */ + plane=0; + src_pixels_0 = (unsigned char *) input_items[plane]; + for (int i = 0; i < noutput_items; i += d_chunk_size){ + noutput_items_produced+=copy_plane_to_surface (plane,d_chunk_size, src_pixels_0); + src_pixels_0 += d_chunk_size; + } + break; + default: //0 or more then 3 channels + std::cerr << "video_sdl_sink_uc: Wrong number of channels: "; + std::cerr <<"1, 2 or 3 channels are supported.\n Requested number of channels is "<< input_items.size () <<"\n"; + throw std::runtime_error ("video_sdl_sink_uc"); + } + + SDL_UnlockYUVOverlay( d_image ); + return noutput_items_produced; +} diff --git a/gr-video-sdl/src/video_sdl_sink_uc.h b/gr-video-sdl/src/video_sdl_sink_uc.h new file mode 100644 index 000000000..3f380f3c4 --- /dev/null +++ b/gr-video-sdl/src/video_sdl_sink_uc.h @@ -0,0 +1,90 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef INCLUDED_VIDEO_SDL_SINK_UC_H +#define INCLUDED_VIDEO_SDL_SINK_UC_H + +#include <gr_sync_block.h> +#include <string> +#include <SDL.h> + +/* fourcc (four character code) */ +#define vid_fourcc(a,b,c,d) (((unsigned)(a)<<0) | ((unsigned)(b)<<8) | ((unsigned)(c)<<16) | ((unsigned)(d)<<24)) +#define IMGFMT_YV12 vid_fourcc('Y','V','1','2') /* 12 YVU 4:2:0 */ + +class video_sdl_sink_uc; +typedef boost::shared_ptr<video_sdl_sink_uc> video_sdl_sink_uc_sptr; + +video_sdl_sink_uc_sptr +video_sdl_make_sink_uc (double framerate,int width=640, int height=480,unsigned int format=IMGFMT_YV12,int dst_width=-1,int dst_height=-1); + +/*! + * \brief video sink using SDL + * + * input signature is one, two or three streams of uchar. + * One stream: stream is grey (Y) + * two streems: first is grey (Y), second is alternating U and V + * Three streams: first is grey (Y), second is U, third is V + * Input samples must be in the range [0,255]. + */ + +class video_sdl_sink_uc : public gr_sync_block { + friend video_sdl_sink_uc_sptr + video_sdl_make_sink_uc (double framerate,int width, int height,unsigned int format,int dst_width,int dst_height); + + int d_chunk_size; + + protected: + video_sdl_sink_uc (double framerate,int width, int height,unsigned int format, + int dst_width,int dst_height); + void copy_line_pixel_interleaved(unsigned char *dst_pixels_u,unsigned char *dst_pixels_v, + const unsigned char * src_pixels,int src_width); + void copy_line_line_interleaved(unsigned char *dst_pixels_u,unsigned char *dst_pixels_v, + const unsigned char * src_pixels,int src_width); + void copy_line_single_plane(unsigned char *dst_pixels,const unsigned char * src_pixels,int src_width); + void copy_line_single_plane_dec2(unsigned char *dst_pixels,const unsigned char * src_pixels,int src_width); + int copy_plane_to_surface (int plane,int noutput_items, + const unsigned char * src_pixels); + float d_framerate; + int d_wanted_frametime_ms; + int d_width; + int d_height; + int d_dst_width; + int d_dst_height; + int d_format; + int d_current_line; + SDL_Surface *d_screen; + SDL_Overlay *d_image; + SDL_Rect d_dst_rect; + float d_avg_delay; + unsigned int d_wanted_ticks; + + + public: + ~video_sdl_sink_uc (); + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif /* INCLUDED_VIDEO_SDL_SINK_UC_H */ |