Imported Upstream version 3.2.2
[debian/gnuradio] / gr-video-sdl / src / video_sdl_sink_s.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2006 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio
6  * 
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <SDL.h>
28
29 #include <video_sdl_sink_s.h>
30 #include <gr_io_signature.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <stdio.h>
36 #include <iostream>
37 #include <stdexcept>
38 #include <string.h>
39
40
41
42 video_sdl_sink_s::video_sdl_sink_s (double framerate,int width, int height,unsigned int format,int dst_width,int dst_height)
43   : gr_sync_block ("video_sdl_sink_s",
44                    gr_make_io_signature (1, 3, sizeof (short)),
45                    gr_make_io_signature (0, 0, 0)),
46     d_chunk_size (width*height),
47     d_framerate(framerate),
48     d_wanted_frametime_ms(0),
49     d_width(width),
50     d_height (height),
51     d_dst_width(dst_width),
52     d_dst_height(dst_height),
53     d_format(format), 
54     d_current_line(0),
55     d_screen(NULL),
56     d_image(NULL),
57     d_avg_delay(0.0),
58     d_wanted_ticks(0)
59 {
60   if(framerate<=0.0) 
61     d_wanted_frametime_ms=0;//Go as fast as possible
62   else
63     d_wanted_frametime_ms=(int)(1000.0/framerate);
64   if(dst_width<0) d_dst_width=d_width;
65   if(dst_height<0) d_dst_height=d_height;
66   if(0==format) d_format=IMGFMT_YV12;
67   
68   atexit(SDL_Quit);//check if this is the way to do this
69   if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
70     std::cerr << "video_sdl_sink_s: Couldn't initialize SDL:" << SDL_GetError() << " \n SDL_Init(SDL_INIT_VIDEO) failed\n";
71     throw std::runtime_error ("video_sdl_sink_s");
72   };
73
74   /* accept any depth */
75   d_screen = SDL_SetVideoMode(dst_width, dst_height, 0, SDL_SWSURFACE|SDL_RESIZABLE|SDL_ANYFORMAT);//SDL_DOUBLEBUF |SDL_SWSURFACE| SDL_HWSURFACE||SDL_FULLSCREEN
76   if ( d_screen == NULL ) {
77     std::cerr << "Unable to set SDL video mode: " << SDL_GetError() <<"\n SDL_SetVideoMode() Failed \n";
78     exit(1);
79   }
80   if ( d_image ) {
81       SDL_FreeYUVOverlay(d_image);
82     }
83   /* Initialize and create the YUV Overlay used for video out */
84   if (!(d_image = SDL_CreateYUVOverlay (d_width, d_height, SDL_YV12_OVERLAY, d_screen))) {
85     std::cerr << "SDL: Couldn't create a YUV overlay: \n"<< SDL_GetError() <<"\n";
86     throw std::runtime_error ("video_sdl_sink_s");
87   }
88
89   printf("SDL screen_mode %d bits-per-pixel\n",
90          d_screen->format->BitsPerPixel);
91   printf("SDL overlay_mode %i \n",
92          d_image->format);
93   d_chunk_size = std::min(1,16384/width); //width*16;
94   d_chunk_size = d_chunk_size*width;
95   //d_chunk_size = (int) (width);
96   set_output_multiple (d_chunk_size);
97   /* Set the default playback area */
98   d_dst_rect.x = 0;
99   d_dst_rect.y = 0;
100   d_dst_rect.w = d_dst_width;
101   d_dst_rect.h = d_dst_height;
102   //clear the surface to grey
103   if ( SDL_LockYUVOverlay( d_image ) ) {
104     std::cerr << "SDL: Couldn't lock YUV overlay: \n"<< SDL_GetError() <<"\n";
105     throw std::runtime_error ("video_sdl_sink_s");
106   }
107   memset(d_image->pixels[0], 128, d_image->pitches[0]*d_height);  
108   memset(d_image->pixels[1], 128, d_image->pitches[1]*d_height/2);  
109   memset(d_image->pixels[2], 128, d_image->pitches[2]*d_height/2);  
110   SDL_UnlockYUVOverlay( d_image );
111 }
112
113 video_sdl_sink_s::~video_sdl_sink_s ()
114 {
115   SDL_Quit();
116 }
117
118 video_sdl_sink_s_sptr
119 video_sdl_make_sink_s (double framerate,int width, int height,unsigned int format,int dst_width,int dst_height)
120 {
121   return video_sdl_sink_s_sptr (new video_sdl_sink_s (framerate, width, height,format,dst_width,dst_height));
122 }
123
124 void
125 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)
126 {
127   for(int i=0;i<src_width;i++)
128   {
129     dst_pixels_u[i]=(unsigned char)src_pixels[i*2];
130     dst_pixels_v[i]=(unsigned char)src_pixels[i*2+1];
131   }
132   return;
133 }
134
135 void
136 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)
137 {
138   for(int i=0;i<src_width;i++)
139   {
140     dst_pixels_u[i]=(unsigned char)src_pixels[i];
141     dst_pixels_v[i]=(unsigned char)src_pixels[i+src_width];
142   }
143   for(int i=src_width;i<src_width*2;i++)
144   {
145     dst_pixels_v[i]=(unsigned char)src_pixels[i];
146   }
147   return;
148 }
149 void
150 video_sdl_sink_s::copy_line_single_plane(unsigned char *dst_pixels,const short * src_pixels,int src_width)
151 {
152   for(int i=0;i<src_width;i++)
153   {
154     dst_pixels[i]=(unsigned char)src_pixels[i];
155   }
156   return;
157 }
158
159 void
160 video_sdl_sink_s::copy_line_single_plane_dec2(unsigned char *dst_pixels,const short * src_pixels,int src_width)
161 {
162   for(int i=0,j=0;i<src_width;i+=2,j++)
163   {
164     dst_pixels[j]=(unsigned char)src_pixels[i];
165   }
166   return;
167 }
168
169 int
170 video_sdl_sink_s::copy_plane_to_surface (int plane,int noutput_items,
171                       const short * src_pixels)
172 {
173     const int first_dst_plane=(12==plane ||1122==plane)?1:plane;
174     const int second_dst_plane=(12==plane ||1122==plane)?2:plane;
175     int current_line=(0==plane)?d_current_line:d_current_line/2;
176     unsigned char * dst_pixels = (unsigned char *)d_image->pixels[first_dst_plane];
177     dst_pixels=&dst_pixels[current_line*d_image->pitches[first_dst_plane]];
178     unsigned char * dst_pixels_2 = (unsigned char *)d_image->pixels[second_dst_plane];
179     dst_pixels_2=&dst_pixels_2[current_line*d_image->pitches[second_dst_plane]];
180     int src_width=(0==plane || 12==plane || 1122==plane)?d_width:d_width/2;
181     int noutput_items_produced=0;
182     int max_height=(0==plane)?d_height-1:d_height/2-1;
183     for (int i = 0; i < noutput_items; i += src_width){
184         //output one line at a time
185         if(12==plane)
186         {
187           copy_line_pixel_interleaved(dst_pixels,dst_pixels_2,src_pixels,src_width);
188           dst_pixels_2 += d_image->pitches[second_dst_plane];
189         }
190         else if (1122==plane)
191           {
192             copy_line_line_interleaved(dst_pixels,dst_pixels_2,src_pixels,src_width);
193             dst_pixels_2 += d_image->pitches[second_dst_plane];
194             src_pixels += src_width;
195           }
196         else if (0==plane)
197           copy_line_single_plane(dst_pixels,src_pixels,src_width);
198         else /* 1==plane || 2==plane*/
199           copy_line_single_plane_dec2(dst_pixels,src_pixels,src_width);//decimate by two horizontally
200
201         src_pixels += src_width;
202         dst_pixels += d_image->pitches[first_dst_plane];
203         noutput_items_produced+=src_width;
204         current_line++;
205         if (current_line>max_height)
206         {
207           //Start new frame
208           //TODO, do this all in a seperate thread
209           current_line=0;
210           dst_pixels=d_image->pixels[first_dst_plane];
211           dst_pixels_2=d_image->pixels[second_dst_plane];
212           if(0==plane)
213           {
214             SDL_DisplayYUVOverlay(d_image, &d_dst_rect);
215             //SDL_Flip(d_screen);
216             unsigned int ticks=SDL_GetTicks();//milliseconds
217             d_wanted_ticks+=d_wanted_frametime_ms;
218             float avg_alpha=0.1;
219             int time_diff=d_wanted_ticks-ticks;
220             d_avg_delay=time_diff*avg_alpha +d_avg_delay*(1.0-avg_alpha);
221           }
222         }
223       }
224     if(0==plane) d_current_line=current_line;
225   return noutput_items_produced;
226 }
227
228 int
229 video_sdl_sink_s::work (int noutput_items,
230                       gr_vector_const_void_star &input_items,
231                       gr_vector_void_star &output_items)
232 {
233   short *src_pixels_0,*src_pixels_1,*src_pixels_2;
234   int noutput_items_produced=0;
235   int plane;
236   int delay=(int)d_avg_delay;
237   if(0==d_wanted_ticks)
238     d_wanted_ticks=SDL_GetTicks();
239   if(delay>0)
240     SDL_Delay((unsigned int)delay);//compensate if running too fast
241
242   if ( SDL_LockYUVOverlay( d_image ) ) {
243     return 0;
244   } 
245   switch (input_items.size ()){
246   case 3:               // first channel=Y, second channel is  U , third channel is V
247     src_pixels_0 = (short *) input_items[0];
248     src_pixels_1 = (short *) input_items[1];
249     src_pixels_2 = (short *) input_items[2];
250     for (int i = 0; i < noutput_items; i += d_chunk_size){
251       copy_plane_to_surface (1,d_chunk_size, src_pixels_1);
252       copy_plane_to_surface (2,d_chunk_size, src_pixels_2);
253       noutput_items_produced+=copy_plane_to_surface (0,d_chunk_size, src_pixels_0);
254       src_pixels_0 += d_chunk_size;
255       src_pixels_1 += d_chunk_size;
256       src_pixels_2 += d_chunk_size;      
257     }
258     break;
259   case 2:
260     if(1) //if(pixel_interleaved_uv)
261     {
262       // first channel=Y, second channel is alternating pixels U and V
263       src_pixels_0 = (short *) input_items[0];
264       src_pixels_1 = (short *) input_items[1];
265       for (int i = 0; i < noutput_items; i += d_chunk_size){
266         copy_plane_to_surface (12,d_chunk_size/2, src_pixels_1);
267         noutput_items_produced+=copy_plane_to_surface (0,d_chunk_size, src_pixels_0);
268         src_pixels_0 += d_chunk_size;
269         src_pixels_1 += d_chunk_size;     
270       }
271     } else
272     {
273       // first channel=Y, second channel is alternating lines U and V
274       src_pixels_0 = (short *) input_items[0];
275       src_pixels_1 = (short *) input_items[1];
276       for (int i = 0; i < noutput_items; i += d_chunk_size){
277         copy_plane_to_surface (1222,d_chunk_size/2, src_pixels_1);
278         noutput_items_produced+=copy_plane_to_surface (0,d_chunk_size, src_pixels_0);
279         src_pixels_0 += d_chunk_size;
280         src_pixels_1 += d_chunk_size;     
281       }
282     }
283     break;
284   case 1:               // grey (Y) input
285     /* Y component */
286     plane=0;
287     src_pixels_0 = (short *) input_items[plane];
288     for (int i = 0; i < noutput_items; i += d_chunk_size){
289       noutput_items_produced+=copy_plane_to_surface (plane,d_chunk_size, src_pixels_0);
290       src_pixels_0 += d_chunk_size;
291     }
292     break;
293   default: //0 or more then 3 channels
294      std::cerr << "video_sdl_sink_s: Wrong number of channels: ";
295      std::cerr <<"1, 2 or 3 channels are supported.\n  Requested number of channels is "<< input_items.size () <<"\n";
296     throw std::runtime_error ("video_sdl_sink_s");
297   }
298
299   SDL_UnlockYUVOverlay( d_image );
300   return noutput_items_produced;
301 }