initial commit
[goodguy/history.git] / cinelerra-5.0 / cinelerra / vdevicev4l.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  * 
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * 
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * 
20  */
21
22
23 #ifdef HAVE_VIDEO4LINUX
24
25 // V4L2 is incompatible with large file support
26 // ALPHA C++ can't compile 64 bit headers
27 #undef _FILE_OFFSET_BITS
28 #undef _LARGEFILE_SOURCE
29 #undef _LARGEFILE64_SOURCE
30
31
32 #include "assets.h"
33 #include "bcsignals.h"
34 #include "channel.h"
35 #include "chantables.h"
36 #include "clip.h"
37 #include "file.h"
38 #include "picture.h"
39 #include "preferences.h"
40 #include "quicktime.h"
41 #include "recordconfig.h"
42 #include "vdevicev4l.h"
43 #include "vframe.h"
44 #include "videodevice.h"
45
46 #include <unistd.h>
47 #include <sys/ioctl.h>
48 #include <fcntl.h>
49 #include <sys/mman.h>
50 #include <string.h>
51
52 VDeviceV4L::VDeviceV4L(VideoDevice *device)
53  : VDeviceBase(device)
54 {
55         initialize();
56 }
57
58 VDeviceV4L::~VDeviceV4L()
59 {
60 }
61
62 int VDeviceV4L::initialize()
63 {
64         capture_buffer = 0;
65         capture_frame_number = 0;
66         read_frame_number = 0;
67         shared_memory = 0;
68         initialization_complete = 0;
69         return 0;
70 }
71
72 int VDeviceV4L::open_input()
73 {
74         device->channel->use_frequency = 1;
75         device->channel->use_fine = 1;
76         device->channel->use_norm = 1;
77         device->channel->use_input = 1;
78
79
80         device->picture->use_brightness = 1;
81         device->picture->use_contrast = 1;
82         device->picture->use_color = 1;
83         device->picture->use_hue = 1;
84         device->picture->use_whiteness = 1;
85
86         if((input_fd = open(device->in_config->v4l_in_device, O_RDWR)) < 0)
87         {
88                 perror("VDeviceV4L::open_input");
89                 return 1;
90         }
91         else
92         {
93                 v4l1_get_inputs();
94                 close(input_fd);
95         }
96         return 0;
97 }
98
99 int VDeviceV4L::close_all()
100 {
101         close_v4l();
102         return 0;
103 }
104
105 int VDeviceV4L::close_v4l()
106 {
107         unmap_v4l_shmem();
108         if(input_fd != -1) close(input_fd);
109         return 0;
110 }
111
112 int VDeviceV4L::unmap_v4l_shmem()
113 {
114         if(capture_buffer)
115         {
116                 if(shared_memory)
117                         munmap(capture_buffer, capture_params.size);
118                 else
119                         delete capture_buffer;
120                 capture_buffer = 0;
121         }
122         return 0;
123 }
124
125 int VDeviceV4L::v4l_init()
126 {
127         input_fd = open(device->in_config->v4l_in_device, O_RDWR);
128
129         if(input_fd < 0)
130                 perror("VDeviceV4L::v4l_init");
131         else
132         {
133                 set_cloexec_flag(input_fd, 1);
134                 set_mute(0);
135                 if(ioctl(input_fd, VIDIOCGWIN, &window_params) < 0)
136                         perror("VDeviceV4L::v4l_init VIDIOCGWIN");
137                 window_params.x = 0;
138                 window_params.y = 0;
139                 window_params.width = device->in_config->w;
140                 window_params.height = device->in_config->h;
141                 window_params.chromakey = 0;
142                 window_params.flags = 0;
143                 window_params.clipcount = 0;
144                 if(ioctl(input_fd, VIDIOCSWIN, &window_params) < 0)
145                         perror("VDeviceV4L::v4l_init VIDIOCSWIN");
146                 if(ioctl(input_fd, VIDIOCGWIN, &window_params) < 0)
147                         perror("VDeviceV4L::v4l_init VIDIOCGWIN");
148
149                 device->in_config->w = window_params.width;
150                 device->in_config->h = window_params.height;
151
152                 PictureConfig picture(0);
153                 set_picture(&picture);
154
155                 if(ioctl(input_fd, VIDIOCGMBUF, &capture_params) < 0)
156                         perror("VDeviceV4L::v4l_init VIDIOCGMBUF");
157
158                 capture_buffer = (char*)mmap(0, 
159                         capture_params.size, 
160                         PROT_READ|PROT_WRITE, 
161                         MAP_SHARED, 
162                         input_fd, 
163                         0);
164
165                 capture_frame_number = 0;
166
167                 if((long)capture_buffer < 0)
168                 {
169 // Use read instead.
170                         perror("VDeviceV4L::v4l_init mmap");
171                         shared_memory = 0;
172                         capture_buffer = new char[capture_params.size];
173                 }
174                 else
175                 {
176 // Get all frames capturing
177                         shared_memory = 1;
178                 }
179         }
180         got_first_frame = 0;
181         return 0;
182 }
183
184 void VDeviceV4L::v4l1_start_capture()
185 {
186         for(int i = 0; i < MIN(capture_params.frames, device->in_config->capture_length); i++)
187                 capture_frame(i);
188 }
189
190
191
192
193
194
195
196
197 int VDeviceV4L::v4l1_get_inputs()
198 {
199         struct video_channel channel_struct;
200         int i = 0, done = 0;
201
202         while(!done && i < 20)
203         {
204                 channel_struct.channel = i;
205                 if(ioctl(input_fd, VIDIOCGCHAN, &channel_struct) < 0)
206                 {
207 // Finished
208                         done = 1;
209                 }
210                 else
211                 {
212                         Channel *channel = new Channel;
213                         strcpy(channel->device_name, channel_struct.name);
214                         device->input_sources.append(channel);
215                 }
216                 i++;
217         }
218         return 0;
219 }
220
221 int VDeviceV4L::set_mute(int muted)
222 {
223 // Open audio, which obviously is controlled by the video driver.
224 // and apparently resets the input source.
225         v4l1_set_mute(muted);
226         return 0;
227 }
228
229 int VDeviceV4L::v4l1_set_mute(int muted)
230 {
231         struct video_audio audio;
232
233     if(ioctl(input_fd, VIDIOCGAUDIO, &audio))
234         if(ioctl(input_fd, VIDIOCGAUDIO, &audio) < 0)
235             perror("VDeviceV4L::ioctl VIDIOCGAUDIO");
236
237         audio.volume = 65535;
238         audio.bass = 65535;
239         audio.treble = 65535;
240         if(muted)
241                 audio.flags |= VIDEO_AUDIO_MUTE | VIDEO_AUDIO_VOLUME;
242         else
243                 audio.flags &= ~VIDEO_AUDIO_MUTE;
244
245     if(ioctl(input_fd, VIDIOCSAUDIO, &audio) < 0)
246                 perror("VDeviceV4L::ioctl VIDIOCSAUDIO");
247         return 0;
248 }
249
250
251 int VDeviceV4L::set_cloexec_flag(int desc, int value)
252 {
253         int oldflags = fcntl(desc, F_GETFD, 0);
254         if(oldflags < 0) return oldflags;
255         if(value != 0) 
256                 oldflags |= FD_CLOEXEC;
257         else
258                 oldflags &= ~FD_CLOEXEC;
259         return fcntl(desc, F_SETFD, oldflags);
260 }
261
262
263
264
265
266 int VDeviceV4L::get_best_colormodel(Asset *asset)
267 {
268         int result = BC_RGB888;
269
270 // Get best colormodel for hardware acceleration
271
272         result = File::get_best_colormodel(asset, device->in_config->driver);
273
274
275 // Need to get color model before opening device but don't call this
276 // unless you want to open the device either.
277         if(!initialization_complete)
278         {
279                 device_colormodel = translate_colormodel(result);
280                 this->colormodel = result;
281                 v4l_init();
282                 initialization_complete = 1;
283         }
284 // printf("VDeviceV4L::get_best_colormodel %c%c%c%c\n", 
285 //      ((char*)&device_colormodel)[0],
286 //      ((char*)&device_colormodel)[1],
287 //      ((char*)&device_colormodel)[2],
288 //      ((char*)&device_colormodel)[3]);
289         return result;
290 }
291
292 unsigned long VDeviceV4L::translate_colormodel(int colormodel)
293 {
294         unsigned long result = 0;
295         switch(colormodel)
296         {
297                 case BC_YUV422:      result = VIDEO_PALETTE_YUV422;      break;
298                 case BC_YUV420P:     result = VIDEO_PALETTE_YUV420P;     break;
299                 case BC_YUV422P:     result = VIDEO_PALETTE_YUV422P;     break;
300                 case BC_YUV411P:     result = VIDEO_PALETTE_YUV411P;     break;
301                 case BC_RGB888:      result = VIDEO_PALETTE_RGB24;       break;
302                 default: result = VIDEO_PALETTE_RGB24; break;
303         }
304 //printf("VDeviceV4L::translate_colormodel %d\n", result);
305         return result;
306 }
307
308 int VDeviceV4L::set_channel(Channel *channel)
309 {
310         return v4l1_set_channel(channel);
311 }
312
313 int VDeviceV4L::v4l1_set_channel(Channel *channel)
314 {
315         struct video_channel channel_struct;
316         struct video_tuner tuner_struct;
317         unsigned long new_freq;
318
319 // Mute changed the input to TV
320 //      set_mute(1);
321
322 //printf("VDeviceV4L::v4l1_set_channel 1 %d\n", channel->input);
323 // Read norm/input defaults
324         channel_struct.channel = channel->input;
325         if(ioctl(input_fd, VIDIOCGCHAN, &channel_struct) < 0)
326                 perror("VDeviceV4L::v4l1_set_channel VIDIOCGCHAN");
327
328 // Set norm/input
329         channel_struct.channel = channel->input;
330         channel_struct.norm = v4l1_get_norm(channel->norm);
331         if(ioctl(input_fd, VIDIOCSCHAN, &channel_struct) < 0)
332                 perror("VDeviceV4L::v4l1_set_channel VIDIOCSCHAN");
333
334         if(channel_struct.flags & VIDEO_VC_TUNER)
335         {
336 // Read tuner defaults
337                 tuner_struct.tuner = channel->input;
338                 if(ioctl(input_fd, VIDIOCGTUNER, &tuner_struct) < 0)
339                         perror("VDeviceV4L::v4l1_set_channel VIDIOCGTUNER");
340
341 // Set tuner
342                 tuner_struct.mode = v4l1_get_norm(channel->norm);
343                 if(ioctl(input_fd, VIDIOCSTUNER, &tuner_struct) < 0)
344                         perror("VDeviceV4L::v4l1_set_channel VIDIOCSTUNER");
345
346                 new_freq = chanlists[channel->freqtable].list[channel->entry].freq;
347                 new_freq = (int)(new_freq * 0.016);
348                 new_freq += channel->fine_tune;
349
350                 if(ioctl(input_fd, VIDIOCSFREQ, &new_freq) < 0)
351                         perror("VDeviceV4L::v4l1_set_channel VIDIOCSFREQ");
352         }
353 //      set_mute(0);
354         return 0;
355 }
356
357 int VDeviceV4L::v4l1_get_norm(int norm)
358 {
359         switch(norm)
360         {
361                 case NTSC:         return VIDEO_MODE_NTSC;         break;
362                 case PAL:          return VIDEO_MODE_PAL;          break;
363                 case SECAM:        return VIDEO_MODE_SECAM;        break;
364         }
365         return 0;
366 }
367
368 int VDeviceV4L::set_picture(PictureConfig *picture)
369 {
370         v4l1_set_picture(picture);
371         return 0;
372 }
373
374
375 int VDeviceV4L::v4l1_set_picture(PictureConfig *picture)
376 {
377         int brightness = (int)((float)picture->brightness / 100 * 32767 + 32768);
378         int hue = (int)((float)picture->hue / 100 * 32767 + 32768);
379         int color = (int)((float)picture->color / 100 * 32767 + 32768);
380         int contrast = (int)((float)picture->contrast / 100 * 32767 + 32768);
381         int whiteness = (int)((float)picture->whiteness / 100 * 32767 + 32768);
382
383         if(ioctl(input_fd, VIDIOCGPICT, &picture_params) < 0)
384                 perror("VDeviceV4L::v4l1_set_picture VIDIOCGPICT");
385         picture_params.brightness = brightness;
386         picture_params.hue = hue;
387         picture_params.colour = color;
388         picture_params.contrast = contrast;
389         picture_params.whiteness = whiteness;
390 // Bogus.  Values are only set in the capture routine.
391         picture_params.depth = 3;
392         picture_params.palette = device_colormodel;
393         if(ioctl(input_fd, VIDIOCSPICT, &picture_params) < 0)
394                 perror("VDeviceV4L::v4l1_set_picture VIDIOCSPICT");
395         if(ioctl(input_fd, VIDIOCGPICT, &picture_params) < 0)
396                 perror("VDeviceV4L::v4l1_set_picture VIDIOCGPICT");
397         return 0;
398 }
399
400
401 int VDeviceV4L::capture_frame(int capture_frame_number)
402 {
403         struct video_mmap params;
404         params.frame = capture_frame_number;
405         params.width = device->in_config->w;
406         params.height = device->in_config->h;
407 // Required to actually set the palette.
408         params.format = device_colormodel;
409 // Tells the driver the buffer is available for writing
410         if(ioctl(input_fd, VIDIOCMCAPTURE, &params) < 0)
411                 perror("VDeviceV4L::capture_frame VIDIOCMCAPTURE");
412         return 0;
413 }
414
415 int VDeviceV4L::wait_v4l_frame()
416 {
417 //printf("VDeviceV4L::wait_v4l_frame 1 %d\n", capture_frame_number);
418         if(ioctl(input_fd, VIDIOCSYNC, &capture_frame_number))
419                 perror("VDeviceV4L::wait_v4l_frame VIDIOCSYNC");
420 //printf("VDeviceV4L::wait_v4l_frame 2 %d\n", capture_frame_number);
421         return 0;
422 }
423
424 int VDeviceV4L::read_v4l_frame(VFrame *frame)
425 {
426         frame_to_vframe(frame, (unsigned char*)capture_buffer + capture_params.offsets[capture_frame_number]);
427         return 0;
428 }
429
430 #ifndef MIN
431 #define MIN(x, y) ((x) < (y) ? (x) : (y))
432 #endif
433
434 int VDeviceV4L::frame_to_vframe(VFrame *frame, unsigned char *input)
435 {
436         int inwidth, inheight;
437         int width, height;
438
439         inwidth = window_params.width;
440         inheight = window_params.height;
441
442         width = MIN(inwidth, frame->get_w());
443         height = MIN(inheight, frame->get_h());
444 //printf("VDeviceV4L::frame_to_vframe %d %d\n", colormodel, frame->get_color_model());
445
446         if(frame->get_color_model() == colormodel)
447         {
448                 switch(frame->get_color_model())
449                 {
450                         case BC_RGB888:
451                         {
452                                 unsigned char *row_in;
453                                 unsigned char *row_out_start, *row_out_end;
454                                 int bytes_per_inrow = inwidth * 3;
455                                 int bytes_per_outrow = frame->get_bytes_per_line();
456                                 unsigned char **rows_out = frame->get_rows();
457
458                                 for(int i = 0; i < frame->get_h(); i++)
459                                 {
460                                         row_in = input + bytes_per_inrow * i;
461                                         row_out_start = rows_out[i];
462                                         row_out_end = row_out_start + 
463                                                 MIN(bytes_per_outrow, bytes_per_inrow);
464
465                                         while(row_out_start < row_out_end)
466                                         {
467                                                 *row_out_start++ = row_in[2];
468                                                 *row_out_start++ = row_in[1];
469                                                 *row_out_start++ = row_in[0];
470                                                 row_in += 3;
471                                         }
472                                 }
473                                 break;
474                         }
475
476                         case BC_YUV420P:
477                         case BC_YUV411P:
478                                 memcpy(frame->get_y(), input, width * height);
479                                 memcpy(frame->get_u(), input + width * height, width * height / 4);
480                                 memcpy(frame->get_v(), input + width * height + width * height / 4, width * height / 4);
481                                 break;
482
483                         case BC_YUV422P:
484                                 memcpy(frame->get_y(), input, width * height);
485                                 memcpy(frame->get_u(), input + width * height, width * height / 2);
486                                 memcpy(frame->get_v(), input + width * height + width * height / 2, width * height / 2);
487                                 break;
488
489                         case BC_YUV422:
490                                 memcpy(frame->get_data(), 
491                                         input, 
492                                         VFrame::calculate_data_size(width, 
493                                                 height, 
494                                                 -1, 
495                                                 frame->get_color_model()));
496                                 break;
497                 }
498         }
499         else
500         {
501                 VFrame *in_frame = new VFrame(input, 
502                         -1,
503                         inwidth, 
504                         inheight, 
505                         colormodel, 
506                         -1);
507                 BC_CModels::transfer(frame->get_rows(), 
508                         in_frame->get_rows(),
509                         frame->get_y(),
510                         frame->get_u(),
511                         frame->get_v(),
512                         in_frame->get_y(),
513                         in_frame->get_u(),
514                         in_frame->get_v(),
515                         0, 
516                         0, 
517                         inwidth, 
518                         inheight,
519                         0, 
520                         0, 
521                         frame->get_w(), 
522                         frame->get_h(),
523                         colormodel, 
524                         frame->get_color_model(),
525                         0,
526                         inwidth,
527                         inheight);
528         }
529         return 0;
530 }
531
532
533
534 int VDeviceV4L::next_frame(int previous_frame)
535 {
536         int result = previous_frame + 1;
537
538         if(result >= MIN(capture_params.frames, device->in_config->capture_length)) result = 0;
539         return result;
540 }
541
542 int VDeviceV4L::read_buffer(VFrame *frame)
543 {
544 SET_TRACE
545         if(shared_memory)
546         {
547 // Read the current frame
548                 if(!got_first_frame) v4l1_start_capture();
549                 wait_v4l_frame();
550                 read_v4l_frame(frame);
551 // Free this frame up for capturing
552                 capture_frame(capture_frame_number);
553 // Advance the frame to capture.
554                 capture_frame_number = next_frame(capture_frame_number);
555         }
556         else
557         {
558                 read(input_fd, capture_buffer, capture_params.size);
559         }
560
561         got_first_frame = 1;
562 SET_TRACE
563         return 0;
564 }
565
566
567
568
569 #endif // HAVE_VIDEO4LINUX
570
571
572
573
574
575