4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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.
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.
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
23 #ifdef HAVE_VIDEO4LINUX
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
33 #include "bcsignals.h"
35 #include "chantables.h"
39 #include "preferences.h"
40 #include "quicktime.h"
41 #include "recordconfig.h"
42 #include "vdevicev4l.h"
44 #include "videodevice.h"
47 #include <sys/ioctl.h>
52 VDeviceV4L::VDeviceV4L(VideoDevice *device)
58 VDeviceV4L::~VDeviceV4L()
62 int VDeviceV4L::initialize()
65 capture_frame_number = 0;
66 read_frame_number = 0;
68 initialization_complete = 0;
72 int VDeviceV4L::open_input()
74 device->channel->use_frequency = 1;
75 device->channel->use_fine = 1;
76 device->channel->use_norm = 1;
77 device->channel->use_input = 1;
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;
86 if((input_fd = open(device->in_config->v4l_in_device, O_RDWR)) < 0)
88 perror("VDeviceV4L::open_input");
99 int VDeviceV4L::close_all()
105 int VDeviceV4L::close_v4l()
108 if(input_fd != -1) close(input_fd);
112 int VDeviceV4L::unmap_v4l_shmem()
117 munmap(capture_buffer, capture_params.size);
119 delete capture_buffer;
125 int VDeviceV4L::v4l_init()
127 input_fd = open(device->in_config->v4l_in_device, O_RDWR);
130 perror("VDeviceV4L::v4l_init");
133 set_cloexec_flag(input_fd, 1);
135 if(ioctl(input_fd, VIDIOCGWIN, &window_params) < 0)
136 perror("VDeviceV4L::v4l_init VIDIOCGWIN");
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");
149 device->in_config->w = window_params.width;
150 device->in_config->h = window_params.height;
152 PictureConfig picture(0);
153 set_picture(&picture);
155 if(ioctl(input_fd, VIDIOCGMBUF, &capture_params) < 0)
156 perror("VDeviceV4L::v4l_init VIDIOCGMBUF");
158 capture_buffer = (char*)mmap(0,
160 PROT_READ|PROT_WRITE,
165 capture_frame_number = 0;
167 if((long)capture_buffer < 0)
170 perror("VDeviceV4L::v4l_init mmap");
172 capture_buffer = new char[capture_params.size];
176 // Get all frames capturing
184 void VDeviceV4L::v4l1_start_capture()
186 for(int i = 0; i < MIN(capture_params.frames, device->in_config->capture_length); i++)
197 int VDeviceV4L::v4l1_get_inputs()
199 struct video_channel channel_struct;
202 while(!done && i < 20)
204 channel_struct.channel = i;
205 if(ioctl(input_fd, VIDIOCGCHAN, &channel_struct) < 0)
212 Channel *channel = new Channel;
213 strcpy(channel->device_name, channel_struct.name);
214 device->input_sources.append(channel);
221 int VDeviceV4L::set_mute(int muted)
223 // Open audio, which obviously is controlled by the video driver.
224 // and apparently resets the input source.
225 v4l1_set_mute(muted);
229 int VDeviceV4L::v4l1_set_mute(int muted)
231 struct video_audio audio;
233 if(ioctl(input_fd, VIDIOCGAUDIO, &audio))
234 if(ioctl(input_fd, VIDIOCGAUDIO, &audio) < 0)
235 perror("VDeviceV4L::ioctl VIDIOCGAUDIO");
237 audio.volume = 65535;
239 audio.treble = 65535;
241 audio.flags |= VIDEO_AUDIO_MUTE | VIDEO_AUDIO_VOLUME;
243 audio.flags &= ~VIDEO_AUDIO_MUTE;
245 if(ioctl(input_fd, VIDIOCSAUDIO, &audio) < 0)
246 perror("VDeviceV4L::ioctl VIDIOCSAUDIO");
251 int VDeviceV4L::set_cloexec_flag(int desc, int value)
253 int oldflags = fcntl(desc, F_GETFD, 0);
254 if(oldflags < 0) return oldflags;
256 oldflags |= FD_CLOEXEC;
258 oldflags &= ~FD_CLOEXEC;
259 return fcntl(desc, F_SETFD, oldflags);
266 int VDeviceV4L::get_best_colormodel(Asset *asset)
268 int result = BC_RGB888;
270 // Get best colormodel for hardware acceleration
272 result = File::get_best_colormodel(asset, device->in_config->driver);
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)
279 device_colormodel = translate_colormodel(result);
280 this->colormodel = result;
282 initialization_complete = 1;
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]);
292 unsigned long VDeviceV4L::translate_colormodel(int colormodel)
294 unsigned long result = 0;
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;
304 //printf("VDeviceV4L::translate_colormodel %d\n", result);
308 int VDeviceV4L::set_channel(Channel *channel)
310 return v4l1_set_channel(channel);
313 int VDeviceV4L::v4l1_set_channel(Channel *channel)
315 struct video_channel channel_struct;
316 struct video_tuner tuner_struct;
317 unsigned long new_freq;
319 // Mute changed the input to TV
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");
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");
334 if(channel_struct.flags & VIDEO_VC_TUNER)
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");
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");
346 new_freq = chanlists[channel->freqtable].list[channel->entry].freq;
347 new_freq = (int)(new_freq * 0.016);
348 new_freq += channel->fine_tune;
350 if(ioctl(input_fd, VIDIOCSFREQ, &new_freq) < 0)
351 perror("VDeviceV4L::v4l1_set_channel VIDIOCSFREQ");
357 int VDeviceV4L::v4l1_get_norm(int norm)
361 case NTSC: return VIDEO_MODE_NTSC; break;
362 case PAL: return VIDEO_MODE_PAL; break;
363 case SECAM: return VIDEO_MODE_SECAM; break;
368 int VDeviceV4L::set_picture(PictureConfig *picture)
370 v4l1_set_picture(picture);
375 int VDeviceV4L::v4l1_set_picture(PictureConfig *picture)
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);
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");
401 int VDeviceV4L::capture_frame(int capture_frame_number)
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, ¶ms) < 0)
411 perror("VDeviceV4L::capture_frame VIDIOCMCAPTURE");
415 int VDeviceV4L::wait_v4l_frame()
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);
424 int VDeviceV4L::read_v4l_frame(VFrame *frame)
426 frame_to_vframe(frame, (unsigned char*)capture_buffer + capture_params.offsets[capture_frame_number]);
431 #define MIN(x, y) ((x) < (y) ? (x) : (y))
434 int VDeviceV4L::frame_to_vframe(VFrame *frame, unsigned char *input)
436 int inwidth, inheight;
439 inwidth = window_params.width;
440 inheight = window_params.height;
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());
446 if(frame->get_color_model() == colormodel)
448 switch(frame->get_color_model())
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();
458 for(int i = 0; i < frame->get_h(); i++)
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);
465 while(row_out_start < row_out_end)
467 *row_out_start++ = row_in[2];
468 *row_out_start++ = row_in[1];
469 *row_out_start++ = row_in[0];
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);
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);
490 memcpy(frame->get_data(),
492 VFrame::calculate_data_size(width,
495 frame->get_color_model()));
501 VFrame *in_frame = new VFrame(input,
507 BC_CModels::transfer(frame->get_rows(),
508 in_frame->get_rows(),
524 frame->get_color_model(),
534 int VDeviceV4L::next_frame(int previous_frame)
536 int result = previous_frame + 1;
538 if(result >= MIN(capture_params.frames, device->in_config->capture_length)) result = 0;
542 int VDeviceV4L::read_buffer(VFrame *frame)
547 // Read the current frame
548 if(!got_first_frame) v4l1_start_capture();
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);
558 read(input_fd, capture_buffer, capture_params.size);
569 #endif // HAVE_VIDEO4LINUX