r999: maintainers added to README_en.
[cinelerra_cv/mob.git] / cinelerra / vdevicev4l.C
blob8554ec9379d11c7ebeabf93e60ae7e8a9abf9fd5
1 // V4L2 is incompatible with large file support
2 // ALPHA C++ can't compile 64 bit headers
3 #undef _FILE_OFFSET_BITS
4 #undef _LARGEFILE_SOURCE
5 #undef _LARGEFILE64_SOURCE
8 #include "assets.h"
9 #include "bcsignals.h"
10 #include "channel.h"
11 #include "chantables.h"
12 #include "clip.h"
13 #include "file.h"
14 #include "picture.h"
15 #include "preferences.h"
16 #include "quicktime.h"
17 #include "recordconfig.h"
18 #include "vdevicev4l.h"
19 #include "vframe.h"
20 #include "videodevice.h"
22 #include <unistd.h>
23 #include <sys/ioctl.h>
24 #include <fcntl.h>
25 #include <sys/mman.h>
26 #include <string.h>
28 VDeviceV4L::VDeviceV4L(VideoDevice *device)
29  : VDeviceBase(device)
31         initialize();
34 VDeviceV4L::~VDeviceV4L()
38 int VDeviceV4L::initialize()
40         capture_buffer = 0;
41         capture_frame_number = 0;
42         read_frame_number = 0;
43         shared_memory = 0;
44         initialization_complete = 0;
45         return 0;
48 int VDeviceV4L::open_input()
50         device->channel->use_frequency = 1;
51         device->channel->use_fine = 1;
52         device->channel->use_norm = 1;
53         device->channel->use_input = 1;
56         device->picture->use_brightness = 1;
57         device->picture->use_contrast = 1;
58         device->picture->use_color = 1;
59         device->picture->use_hue = 1;
60         device->picture->use_whiteness = 1;
62         if((input_fd = open(device->in_config->v4l_in_device, O_RDWR)) < 0)
63         {
64                 perror("VDeviceV4L::open_input");
65                 return 1;
66         }
67         else
68         {
69                 v4l1_get_inputs();
70                 close(input_fd);
71         }
72         return 0;
75 int VDeviceV4L::close_all()
77         close_v4l();
78         return 0;
81 int VDeviceV4L::close_v4l()
83         unmap_v4l_shmem();
84         if(input_fd != -1) close(input_fd);
85         return 0;
88 int VDeviceV4L::unmap_v4l_shmem()
90         if(capture_buffer)
91         {
92                 if(shared_memory)
93                         munmap(capture_buffer, capture_params.size);
94                 else
95                         delete capture_buffer;
96                 capture_buffer = 0;
97         }
98         return 0;
101 int VDeviceV4L::v4l_init()
103         int i;
105         input_fd = open(device->in_config->v4l_in_device, O_RDWR);
107         if(input_fd < 0)
108                 perror("VDeviceV4L::v4l_init");
109         else
110         {
111                 set_cloexec_flag(input_fd, 1);
112                 set_mute(0);
113                 if(ioctl(input_fd, VIDIOCGWIN, &window_params) < 0)
114                         perror("VDeviceV4L::v4l_init VIDIOCGWIN");
115                 window_params.x = 0;
116                 window_params.y = 0;
117                 window_params.width = device->in_config->w;
118                 window_params.height = device->in_config->h;
119                 window_params.chromakey = 0;
120                 window_params.flags = 0;
121                 window_params.clipcount = 0;
122                 if(ioctl(input_fd, VIDIOCSWIN, &window_params) < 0)
123                         perror("VDeviceV4L::v4l_init VIDIOCSWIN");
124                 if(ioctl(input_fd, VIDIOCGWIN, &window_params) < 0)
125                         perror("VDeviceV4L::v4l_init VIDIOCGWIN");
127                 device->in_config->w = window_params.width;
128                 device->in_config->h = window_params.height;
130                 PictureConfig picture(0);
131                 set_picture(&picture);
133                 if(ioctl(input_fd, VIDIOCGMBUF, &capture_params) < 0)
134                         perror("VDeviceV4L::v4l_init VIDIOCGMBUF");
136                 capture_buffer = (char*)mmap(0, 
137                         capture_params.size, 
138                         PROT_READ|PROT_WRITE, 
139                         MAP_SHARED, 
140                         input_fd, 
141                         0);
143                 capture_frame_number = 0;
145                 if((long)capture_buffer < 0)
146                 {
147 // Use read instead.
148                         perror("VDeviceV4L::v4l_init mmap");
149                         shared_memory = 0;
150                         capture_buffer = new char[capture_params.size];
151                 }
152                 else
153                 {
154 // Get all frames capturing
155                         shared_memory = 1;
156                 }
157         }
158         got_first_frame = 0;
159         return 0;
162 void VDeviceV4L::v4l1_start_capture()
164         for(int i = 0; i < MIN(capture_params.frames, device->in_config->capture_length); i++)
165                 capture_frame(i);
175 int VDeviceV4L::v4l1_get_inputs()
177         struct video_channel channel_struct;
178         int i = 0, done = 0;
179         char *new_source;
181         while(!done && i < 20)
182         {
183                 channel_struct.channel = i;
184                 if(ioctl(input_fd, VIDIOCGCHAN, &channel_struct) < 0)
185                 {
186 // Finished
187                         done = 1;
188                 }
189                 else
190                 {
191                         Channel *channel = new Channel;
192                         strcpy(channel->device_name, channel_struct.name);
193                         device->input_sources.append(channel);
194                 }
195                 i++;
196         }
197         return 0;
200 int VDeviceV4L::set_mute(int muted)
202 // Open audio, which obviously is controlled by the video driver.
203 // and apparently resets the input source.
204         v4l1_set_mute(muted);
207 int VDeviceV4L::v4l1_set_mute(int muted)
209         struct video_audio audio;
211     if(ioctl(input_fd, VIDIOCGAUDIO, &audio))
212         if(ioctl(input_fd, VIDIOCGAUDIO, &audio) < 0)
213             perror("VDeviceV4L::ioctl VIDIOCGAUDIO");
215         audio.volume = 65535;
216         audio.bass = 65535;
217         audio.treble = 65535;
218         if(muted)
219                 audio.flags |= VIDEO_AUDIO_MUTE | VIDEO_AUDIO_VOLUME;
220         else
221                 audio.flags &= ~VIDEO_AUDIO_MUTE;
223     if(ioctl(input_fd, VIDIOCSAUDIO, &audio) < 0)
224                 perror("VDeviceV4L::ioctl VIDIOCSAUDIO");
225         return 0;
229 int VDeviceV4L::set_cloexec_flag(int desc, int value)
231         int oldflags = fcntl(desc, F_GETFD, 0);
232         if(oldflags < 0) return oldflags;
233         if(value != 0) 
234                 oldflags |= FD_CLOEXEC;
235         else
236                 oldflags &= ~FD_CLOEXEC;
237         return fcntl(desc, F_SETFD, oldflags);
244 int VDeviceV4L::get_best_colormodel(Asset *asset)
246         int result = BC_RGB888;
248 // Get best colormodel for hardware acceleration
250         result = File::get_best_colormodel(asset, device->in_config->driver);
253 // Need to get color model before opening device but don't call this
254 // unless you want to open the device either.
255         if(!initialization_complete)
256         {
257                 device_colormodel = translate_colormodel(result);
258                 this->colormodel = result;
259                 v4l_init();
260                 initialization_complete = 1;
261         }
262 // printf("VDeviceV4L::get_best_colormodel %c%c%c%c\n", 
263 //      ((char*)&device_colormodel)[0],
264 //      ((char*)&device_colormodel)[1],
265 //      ((char*)&device_colormodel)[2],
266 //      ((char*)&device_colormodel)[3]);
267         return result;
270 unsigned long VDeviceV4L::translate_colormodel(int colormodel)
272         unsigned long result = 0;
273         switch(colormodel)
274         {
275                 case BC_YUV422:      result = VIDEO_PALETTE_YUV422;      break;
276                 case BC_YUV420P:     result = VIDEO_PALETTE_YUV420P;     break;
277                 case BC_YUV422P:     result = VIDEO_PALETTE_YUV422P;     break;
278                 case BC_YUV411P:     result = VIDEO_PALETTE_YUV411P;     break;
279                 case BC_RGB888:      result = VIDEO_PALETTE_RGB24;       break;
280                 default: result = VIDEO_PALETTE_RGB24; break;
281         }
282 //printf("VDeviceV4L::translate_colormodel %d\n", result);
283         return result;
286 int VDeviceV4L::set_channel(Channel *channel)
288         return v4l1_set_channel(channel);
291 int VDeviceV4L::v4l1_set_channel(Channel *channel)
293         struct video_channel channel_struct;
294         struct video_tuner tuner_struct;
295         unsigned long new_freq;
297 // Mute changed the input to TV
298 //      set_mute(1);
300 //printf("VDeviceV4L::v4l1_set_channel 1 %d\n", channel->input);
301 // Read norm/input defaults
302         channel_struct.channel = channel->input;
303         if(ioctl(input_fd, VIDIOCGCHAN, &channel_struct) < 0)
304                 perror("VDeviceV4L::v4l1_set_channel VIDIOCGCHAN");
306 // Set norm/input
307         channel_struct.channel = channel->input;
308         channel_struct.norm = v4l1_get_norm(channel->norm);
309         if(ioctl(input_fd, VIDIOCSCHAN, &channel_struct) < 0)
310                 perror("VDeviceV4L::v4l1_set_channel VIDIOCSCHAN");
312         if(channel_struct.flags & VIDEO_VC_TUNER)
313         {
314 // Read tuner defaults
315                 tuner_struct.tuner = channel->input;
316                 if(ioctl(input_fd, VIDIOCGTUNER, &tuner_struct) < 0)
317                         perror("VDeviceV4L::v4l1_set_channel VIDIOCGTUNER");
319 // Set tuner
320                 tuner_struct.mode = v4l1_get_norm(channel->norm);
321                 if(ioctl(input_fd, VIDIOCSTUNER, &tuner_struct) < 0)
322                         perror("VDeviceV4L::v4l1_set_channel VIDIOCSTUNER");
324                 new_freq = chanlists[channel->freqtable].list[channel->entry].freq;
325                 new_freq = (int)(new_freq * 0.016);
326                 new_freq += channel->fine_tune;
328                 if(ioctl(input_fd, VIDIOCSFREQ, &new_freq) < 0)
329                         perror("VDeviceV4L::v4l1_set_channel VIDIOCSFREQ");
330         }
331 //      set_mute(0);
332         return 0;
335 int VDeviceV4L::v4l1_get_norm(int norm)
337         switch(norm)
338         {
339                 case NTSC:         return VIDEO_MODE_NTSC;         break;
340                 case PAL:          return VIDEO_MODE_PAL;          break;
341                 case SECAM:        return VIDEO_MODE_SECAM;        break;
342         }
343         return 0;
346 int VDeviceV4L::set_picture(PictureConfig *picture)
348         v4l1_set_picture(picture);
352 int VDeviceV4L::v4l1_set_picture(PictureConfig *picture)
354         int brightness = (int)((float)picture->brightness / 100 * 32767 + 32768);
355         int hue = (int)((float)picture->hue / 100 * 32767 + 32768);
356         int color = (int)((float)picture->color / 100 * 32767 + 32768);
357         int contrast = (int)((float)picture->contrast / 100 * 32767 + 32768);
358         int whiteness = (int)((float)picture->whiteness / 100 * 32767 + 32768);
360         if(ioctl(input_fd, VIDIOCGPICT, &picture_params) < 0)
361                 perror("VDeviceV4L::v4l1_set_picture VIDIOCGPICT");
362         picture_params.brightness = brightness;
363         picture_params.hue = hue;
364         picture_params.colour = color;
365         picture_params.contrast = contrast;
366         picture_params.whiteness = whiteness;
367 // Bogus.  Values are only set in the capture routine.
368         picture_params.depth = 3;
369         picture_params.palette = device_colormodel;
370         if(ioctl(input_fd, VIDIOCSPICT, &picture_params) < 0)
371                 perror("VDeviceV4L::v4l1_set_picture VIDIOCSPICT");
372         if(ioctl(input_fd, VIDIOCGPICT, &picture_params) < 0)
373                 perror("VDeviceV4L::v4l1_set_picture VIDIOCGPICT");
374         return 0;
378 int VDeviceV4L::capture_frame(int capture_frame_number)
380         struct video_mmap params;
381         params.frame = capture_frame_number;
382         params.width = device->in_config->w;
383         params.height = device->in_config->h;
384 // Required to actually set the palette.
385         params.format = device_colormodel;
386 // Tells the driver the buffer is available for writing
387         if(ioctl(input_fd, VIDIOCMCAPTURE, &params) < 0)
388                 perror("VDeviceV4L::capture_frame VIDIOCMCAPTURE");
389         return 0;
392 int VDeviceV4L::wait_v4l_frame()
394 //printf("VDeviceV4L::wait_v4l_frame 1 %d\n", capture_frame_number);
395         if(ioctl(input_fd, VIDIOCSYNC, &capture_frame_number))
396                 perror("VDeviceV4L::wait_v4l_frame VIDIOCSYNC");
397 //printf("VDeviceV4L::wait_v4l_frame 2 %d\n", capture_frame_number);
398         return 0;
401 int VDeviceV4L::read_v4l_frame(VFrame *frame)
403         frame_to_vframe(frame, (unsigned char*)capture_buffer + capture_params.offsets[capture_frame_number]);
404         return 0;
407 #ifndef MIN
408 #define MIN(x, y) ((x) < (y) ? (x) : (y))
409 #endif
411 int VDeviceV4L::frame_to_vframe(VFrame *frame, unsigned char *input)
413         int inwidth, inheight;
414         int width, height;
416         inwidth = window_params.width;
417         inheight = window_params.height;
419         width = MIN(inwidth, frame->get_w());
420         height = MIN(inheight, frame->get_h());
421 //printf("VDeviceV4L::frame_to_vframe %d %d\n", colormodel, frame->get_color_model());
423         if(frame->get_color_model() == colormodel)
424         {
425                 switch(frame->get_color_model())
426                 {
427                         case BC_RGB888:
428                         {
429                                 unsigned char *row_in;
430                                 unsigned char *row_out_start, *row_out_end;
431                                 int bytes_per_inrow = inwidth * 3;
432                                 int bytes_per_outrow = frame->get_bytes_per_line();
433                                 unsigned char **rows_out = frame->get_rows();
435                                 for(int i = 0; i < frame->get_h(); i++)
436                                 {
437                                         row_in = input + bytes_per_inrow * i;
438                                         row_out_start = rows_out[i];
439                                         row_out_end = row_out_start + 
440                                                 MIN(bytes_per_outrow, bytes_per_inrow);
442                                         while(row_out_start < row_out_end)
443                                         {
444                                                 *row_out_start++ = row_in[2];
445                                                 *row_out_start++ = row_in[1];
446                                                 *row_out_start++ = row_in[0];
447                                                 row_in += 3;
448                                         }
449                                 }
450                                 break;
451                         }
453                         case BC_YUV420P:
454                         case BC_YUV411P:
455                                 memcpy(frame->get_y(), input, width * height);
456                                 memcpy(frame->get_u(), input + width * height, width * height / 4);
457                                 memcpy(frame->get_v(), input + width * height + width * height / 4, width * height / 4);
458                                 break;
460                         case BC_YUV422P:
461                                 memcpy(frame->get_y(), input, width * height);
462                                 memcpy(frame->get_u(), input + width * height, width * height / 2);
463                                 memcpy(frame->get_v(), input + width * height + width * height / 2, width * height / 2);
464                                 break;
466                         case BC_YUV422:
467                                 memcpy(frame->get_data(), 
468                                         input, 
469                                         VFrame::calculate_data_size(width, 
470                                                 height, 
471                                                 -1, 
472                                                 frame->get_color_model()));
473                                 break;
474                 }
475         }
476         else
477         {
478                 VFrame *in_frame = new VFrame(input, 
479                         inwidth, 
480                         inheight, 
481                         colormodel, 
482                         -1);
483                 cmodel_transfer(frame->get_rows(), 
484                         in_frame->get_rows(),
485                         frame->get_y(),
486                         frame->get_u(),
487                         frame->get_v(),
488                         in_frame->get_y(),
489                         in_frame->get_u(),
490                         in_frame->get_v(),
491                         0, 
492                         0, 
493                         inwidth, 
494                         inheight,
495                         0, 
496                         0, 
497                         frame->get_w(), 
498                         frame->get_h(),
499                         colormodel, 
500                         frame->get_color_model(),
501                         0,
502                         inwidth,
503                         inheight);
504         }
505         return 0;
510 int VDeviceV4L::next_frame(int previous_frame)
512         int result = previous_frame + 1;
514         if(result >= MIN(capture_params.frames, device->in_config->capture_length)) result = 0;
515         return result;
518 int VDeviceV4L::read_buffer(VFrame *frame)
520         int result = 0;
522 SET_TRACE
523         if(shared_memory)
524         {
525 // Read the current frame
526                 if(!got_first_frame) v4l1_start_capture();
527                 wait_v4l_frame();
528                 read_v4l_frame(frame);
529 // Free this frame up for capturing
530                 capture_frame(capture_frame_number);
531 // Advance the frame to capture.
532                 capture_frame_number = next_frame(capture_frame_number);
533         }
534         else
535         {
536                 read(input_fd, capture_buffer, capture_params.size);
537         }
538         got_first_frame = 1;
539 SET_TRACE
541         return 0;