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 // ALPHA C++ can't compile 64 bit headers
26 #undef _LARGEFILE_SOURCE
27 #undef _LARGEFILE64_SOURCE
28 #undef _FILE_OFFSET_BITS
31 #include "bcsignals.h"
33 #include "chantables.h"
34 #include "condition.h"
38 #include "playbackconfig.h"
39 #include "preferences.h"
40 #include "recordconfig.h"
41 #include "strategies.inc"
42 #include "vdevicebuz.h"
44 #include "videoconfig.h"
45 #include "videodevice.h"
49 #include <linux/kernel.h>
50 //#include <linux/videodev2.h>
51 #include <linux/videodev.h>
53 #include <sys/ioctl.h>
59 #define READ_TIMEOUT 5000000
62 VDeviceBUZInput::VDeviceBUZInput(VDeviceBUZ *device)
65 this->device = device;
70 current_outbuffer = 0;
72 output_lock = new Condition(0, "VDeviceBUZInput::output_lock");
73 buffer_lock = new Mutex("VDeviceBUZInput::buffer_lock");
76 VDeviceBUZInput::~VDeviceBUZInput()
87 for(int i = 0; i < total_buffers; i++)
92 delete [] buffer_size;
98 void VDeviceBUZInput::start()
101 total_buffers = device->device->in_config->capture_length;
102 buffer = new char*[total_buffers];
103 buffer_size = new int[total_buffers];
104 bzero(buffer_size, sizeof(int) * total_buffers);
105 for(int i = 0; i < total_buffers; i++)
107 buffer[i] = new char[INPUT_BUFFER_SIZE];
113 void VDeviceBUZInput::run()
115 struct buz_sync bsync;
120 Thread::enable_cancel();
121 if(ioctl(device->jvideo_fd, BUZIOC_SYNC, &bsync) < 0)
123 perror("VDeviceBUZInput::run BUZIOC_SYNC");
125 Thread::disable_cancel();
129 Thread::disable_cancel();
134 buffer_lock->lock("VDeviceBUZInput::run");
135 // Save only if the current buffer is free.
136 if(!buffer_size[current_inbuffer])
139 // Copy to input buffer
140 memcpy(buffer[current_inbuffer],
141 device->input_buffer + bsync.frame * device->breq.size,
144 // Advance input buffer number and decrease semaphore.
145 buffer_size[current_inbuffer] = bsync.length;
146 increment_counter(¤t_inbuffer);
149 buffer_lock->unlock();
151 if(ioctl(device->jvideo_fd, BUZIOC_QBUF_CAPT, &bsync.frame))
152 perror("VDeviceBUZInput::run BUZIOC_QBUF_CAPT");
154 if(new_buffer) output_lock->unlock();
159 void VDeviceBUZInput::get_buffer(char **ptr, int *size)
161 // Increase semaphore to wait for buffer.
162 int result = output_lock->timed_lock(READ_TIMEOUT, "VDeviceBUZInput::get_buffer");
165 // The driver has its own timeout routine but it doesn't work because
166 // because the tuner lock is unlocked and relocked with no delay.
168 // output_lock->lock("VDeviceBUZInput::get_buffer");
172 // Take over buffer table
173 buffer_lock->lock("VDeviceBUZInput::get_buffer");
174 *ptr = buffer[current_outbuffer];
175 *size = buffer_size[current_outbuffer];
176 buffer_lock->unlock();
180 //printf("VDeviceBUZInput::get_buffer 1\n");
181 output_lock->unlock();
185 void VDeviceBUZInput::put_buffer()
187 buffer_lock->lock("VDeviceBUZInput::put_buffer");
188 buffer_size[current_outbuffer] = 0;
189 buffer_lock->unlock();
190 increment_counter(¤t_outbuffer);
193 void VDeviceBUZInput::increment_counter(int *counter)
196 if(*counter >= total_buffers) *counter = 0;
199 void VDeviceBUZInput::decrement_counter(int *counter)
202 if(*counter < 0) *counter = total_buffers - 1;
219 VDeviceBUZ::VDeviceBUZ(VideoDevice *device)
220 : VDeviceBase(device)
223 render_strategies.append(VRENDER_MJPG);
224 tuner_lock = new Mutex("VDeviceBUZ::tuner_lock");
227 VDeviceBUZ::~VDeviceBUZ()
233 int VDeviceBUZ::reset_parameters()
257 int VDeviceBUZ::close_input_core()
268 if(jvideo_fd) close(jvideo_fd);
275 munmap(input_buffer, breq.count * breq.size);
281 int VDeviceBUZ::close_output_core()
283 //printf("VDeviceBUZ::close_output_core 1\n");
286 // if(ioctl(jvideo_fd, BUZIOC_QBUF_PLAY, &n) < 0)
287 // perror("VDeviceBUZ::close_output_core BUZIOC_QBUF_PLAY");
288 if(jvideo_fd) close(jvideo_fd);
293 if(output_buffer > 0)
294 munmap(output_buffer, breq.count * breq.size);
312 //printf("VDeviceBUZ::close_output_core 2\n");
317 int VDeviceBUZ::close_all()
319 //printf("VDeviceBUZ::close_all 1\n");
321 //printf("VDeviceBUZ::close_all 1\n");
323 //printf("VDeviceBUZ::close_all 1\n");
324 if(frame_buffer) delete frame_buffer;
325 //printf("VDeviceBUZ::close_all 1\n");
327 //printf("VDeviceBUZ::close_all 2\n");
331 #define COMPOSITE_TEXT "Composite"
332 #define SVIDEO_TEXT "S-Video"
333 #define BUZ_COMPOSITE 0
336 void VDeviceBUZ::get_inputs(ArrayList<Channel*> *input_sources)
338 Channel *new_source = new Channel;
340 new_source = new Channel;
341 strcpy(new_source->device_name, COMPOSITE_TEXT);
342 input_sources->append(new_source);
344 new_source = new Channel;
345 strcpy(new_source->device_name, SVIDEO_TEXT);
346 input_sources->append(new_source);
349 int VDeviceBUZ::open_input()
351 device->channel->use_norm = 1;
352 device->channel->use_input = 1;
354 device->picture->use_brightness = 1;
355 device->picture->use_contrast = 1;
356 device->picture->use_color = 1;
357 device->picture->use_hue = 1;
358 device->picture->use_whiteness = 1;
360 // Can't open input until after the channel is set
364 int VDeviceBUZ::open_output()
366 // Can't open output until after the channel is set
370 int VDeviceBUZ::set_channel(Channel *channel)
372 if(!channel) return 0;
374 tuner_lock->lock("VDeviceBUZ::set_channel");
379 open_input_core(channel);
384 open_output_core(channel);
387 tuner_lock->unlock();
393 int VDeviceBUZ::create_channeldb(ArrayList<Channel*> *channeldb)
398 int VDeviceBUZ::set_picture(PictureConfig *picture)
400 this->brightness = (int)((float)picture->brightness / 100 * 32767 + 32768);
401 this->hue = (int)((float)picture->hue / 100 * 32767 + 32768);
402 this->color = (int)((float)picture->color / 100 * 32767 + 32768);
403 this->contrast = (int)((float)picture->contrast / 100 * 32767 + 32768);
404 this->whiteness = (int)((float)picture->whiteness / 100 * 32767 + 32768);
407 tuner_lock->lock("VDeviceBUZ::set_picture");
418 tuner_lock->unlock();
421 // TRACE("VDeviceBUZ::set_picture 1");
422 // tuner_lock->lock("VDeviceBUZ::set_picture");
423 // TRACE("VDeviceBUZ::set_picture 2");
427 // struct video_picture picture_params;
428 // // This call takes a long time in 2.4.22
429 // if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0)
430 // perror("VDeviceBUZ::set_picture VIDIOCGPICT");
431 // picture_params.brightness = brightness;
432 // picture_params.hue = hue;
433 // picture_params.colour = color;
434 // picture_params.contrast = contrast;
435 // picture_params.whiteness = whiteness;
436 // // This call takes a long time in 2.4.22
437 // if(ioctl(jvideo_fd, VIDIOCSPICT, &picture_params) < 0)
438 // perror("VDeviceBUZ::set_picture VIDIOCSPICT");
439 // if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0)
440 // perror("VDeviceBUZ::set_picture VIDIOCGPICT");
443 // TRACE("VDeviceBUZ::set_picture 10");
446 // tuner_lock->unlock();
451 int VDeviceBUZ::get_norm(int norm)
455 case NTSC: return VIDEO_MODE_NTSC;
456 case PAL: return VIDEO_MODE_PAL;
457 case SECAM: return VIDEO_MODE_SECAM;
459 printf("VDeviceBUZ::get_norm: unknown norm %d\n", norm);
460 return VIDEO_MODE_NTSC;
463 int VDeviceBUZ::read_buffer(VFrame *frame)
465 tuner_lock->lock("VDeviceBUZ::read_buffer");
466 if(!jvideo_fd) open_input_core(0);
468 // Get buffer from thread
472 input_thread->get_buffer(&buffer, &buffer_size);
476 frame->allocate_compressed_data(buffer_size);
477 frame->set_compressed_size(buffer_size);
479 // Transfer fields to frame
480 if(device->odd_field_first)
482 long field2_offset = mjpeg_get_field2((unsigned char*)buffer, buffer_size);
483 long field1_len = field2_offset;
484 long field2_len = buffer_size - field2_offset;
486 memcpy(frame->get_data(), buffer + field2_offset, field2_len);
487 memcpy(frame->get_data() + field2_len, buffer, field1_len);
491 bcopy(buffer, frame->get_data(), buffer_size);
494 input_thread->put_buffer();
495 tuner_lock->unlock();
499 tuner_lock->unlock();
501 // Allow other threads to lock the tuner_lock under NPTL.
509 int VDeviceBUZ::open_input_core(Channel *channel)
511 jvideo_fd = open(device->in_config->buz_in_device, O_RDONLY);
515 fprintf(stderr, "VDeviceBUZ::open_input %s: %s\n",
516 device->in_config->buz_in_device,
522 // Create input sources
523 get_inputs(&device->input_sources);
525 // Set current input source
528 for(int i = 0; i < 2; i++)
530 struct video_channel vch;
531 vch.channel = channel->input;
532 vch.norm = get_norm(channel->norm);
534 //printf("VDeviceBUZ::open_input_core 2 %d %d\n", vch.channel, vch.norm);
535 if(ioctl(jvideo_fd, VIDIOCSCHAN, &vch) < 0)
536 perror("VDeviceBUZ::open_input_core VIDIOCSCHAN ");
542 // struct video_capability vc;
543 // if(ioctl(jvideo_fd, VIDIOCGCAP, &vc) < 0)
544 // perror("VDeviceBUZ::open_input VIDIOCGCAP");
546 // API dependant initialization
547 if(ioctl(jvideo_fd, BUZIOC_G_PARAMS, &bparm) < 0)
548 perror("VDeviceBUZ::open_input BUZIOC_G_PARAMS");
553 bparm.field_per_buff = 2;
554 bparm.img_width = device->in_config->w;
555 bparm.img_height = device->in_config->h / bparm.field_per_buff;
559 // bparm.APP_len = 14;
562 bparm.decimation = 0;
563 bparm.quality = device->quality;
564 bzero(bparm.APP_data, sizeof(bparm.APP_data));
566 if(ioctl(jvideo_fd, BUZIOC_S_PARAMS, &bparm) < 0)
567 perror("VDeviceBUZ::open_input BUZIOC_S_PARAMS");
569 // printf("open_input %d %d %d %d %d %d %d %d %d %d %d %d\n",
573 // bparm.field_per_buff,
583 breq.count = device->in_config->capture_length;
584 breq.size = INPUT_BUFFER_SIZE;
585 if(ioctl(jvideo_fd, BUZIOC_REQBUFS, &breq) < 0)
586 perror("VDeviceBUZ::open_input BUZIOC_REQBUFS");
588 //printf("open_input %s %d %d %d %d\n", device->in_config->buz_in_device, breq.count, breq.size, bparm.img_width, bparm.img_height);
589 if((input_buffer = (char*)mmap(0,
590 breq.count * breq.size,
595 perror("VDeviceBUZ::open_input mmap");
598 // Set picture quality
599 struct video_picture picture_params;
600 // This call takes a long time in 2.4.22
601 if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0)
602 perror("VDeviceBUZ::set_picture VIDIOCGPICT");
603 picture_params.brightness = brightness;
604 picture_params.hue = hue;
605 picture_params.colour = color;
606 picture_params.contrast = contrast;
607 picture_params.whiteness = whiteness;
608 // This call takes a long time in 2.4.22
609 if(ioctl(jvideo_fd, VIDIOCSPICT, &picture_params) < 0)
610 perror("VDeviceBUZ::set_picture VIDIOCSPICT");
611 if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0)
612 perror("VDeviceBUZ::set_picture VIDIOCGPICT");
616 int count = breq.count;
617 for(int i = 0; i < count; i++)
619 if(ioctl(jvideo_fd, BUZIOC_QBUF_CAPT, &i) < 0)
620 perror("VDeviceBUZ::open_input BUZIOC_QBUF_CAPT");
624 input_thread = new VDeviceBUZInput(this);
625 input_thread->start();
626 //printf("VDeviceBUZ::open_input_core 2\n");
630 int VDeviceBUZ::open_output_core(Channel *channel)
632 //printf("VDeviceBUZ::open_output 1\n");
635 jvideo_fd = open(device->out_config->buz_out_device, O_RDWR);
638 perror("VDeviceBUZ::open_output");
643 // Set current input source
646 struct video_channel vch;
647 vch.channel = channel->input;
648 vch.norm = get_norm(channel->norm);
650 if(ioctl(jvideo_fd, VIDIOCSCHAN, &vch) < 0)
651 perror("VDeviceBUZ::open_output_core VIDIOCSCHAN ");
655 breq.size = INPUT_BUFFER_SIZE;
656 if(ioctl(jvideo_fd, BUZIOC_REQBUFS, &breq) < 0)
657 perror("VDeviceBUZ::open_output BUZIOC_REQBUFS");
658 if((output_buffer = (char*)mmap(0,
659 breq.count * breq.size,
660 PROT_READ | PROT_WRITE,
664 perror("VDeviceBUZ::open_output mmap");
666 if(ioctl(jvideo_fd, BUZIOC_G_PARAMS, &bparm) < 0)
667 perror("VDeviceBUZ::open_output BUZIOC_G_PARAMS");
669 bparm.decimation = 1;
671 bparm.field_per_buff = 2;
674 bparm.img_width = device->out_w;
675 bparm.img_height = device->out_h / bparm.field_per_buff;
680 if(ioctl(jvideo_fd, BUZIOC_S_PARAMS, &bparm) < 0)
681 perror("VDeviceBUZ::open_output BUZIOC_S_PARAMS");
682 //printf("VDeviceBUZ::open_output 2\n");
688 int VDeviceBUZ::write_buffer(VFrame *frame, EDL *edl)
690 //printf("VDeviceBUZ::write_buffer 1\n");
691 tuner_lock->lock("VDeviceBUZ::write_buffer");
693 if(!jvideo_fd) open_output_core(0);
696 if(frame->get_color_model() != BC_COMPRESSED)
698 if(!temp_frame) temp_frame = new VFrame;
701 mjpeg = mjpeg_new(device->out_w, device->out_h, 2);
702 mjpeg_set_quality(mjpeg, device->quality);
703 mjpeg_set_float(mjpeg, 0);
706 mjpeg_compress(mjpeg,
711 frame->get_color_model(),
713 temp_frame->allocate_compressed_data(mjpeg_output_size(mjpeg));
714 temp_frame->set_compressed_size(mjpeg_output_size(mjpeg));
715 bcopy(mjpeg_output_buffer(mjpeg), temp_frame->get_data(), mjpeg_output_size(mjpeg));
720 // Wait for frame to become available
721 // Caused close_output_core to lock up.
722 // if(total_loops >= 1)
724 // if(ioctl(jvideo_fd, BUZIOC_SYNC, &output_number) < 0)
725 // perror("VDeviceBUZ::write_buffer BUZIOC_SYNC");
728 if(device->out_config->buz_swap_fields)
730 long field2_offset = mjpeg_get_field2((unsigned char*)ptr->get_data(),
731 ptr->get_compressed_size());
732 long field2_len = ptr->get_compressed_size() - field2_offset;
733 memcpy(output_buffer + output_number * breq.size,
734 ptr->get_data() + field2_offset,
736 memcpy(output_buffer + output_number * breq.size +field2_len,
742 bcopy(ptr->get_data(),
743 output_buffer + output_number * breq.size,
744 ptr->get_compressed_size());
747 if(ioctl(jvideo_fd, BUZIOC_QBUF_PLAY, &output_number) < 0)
748 perror("VDeviceBUZ::write_buffer BUZIOC_QBUF_PLAY");
751 if(output_number >= (int)breq.count)
756 tuner_lock->unlock();
757 //printf("VDeviceBUZ::write_buffer 2\n");
762 void VDeviceBUZ::new_output_buffer(VFrame *output,
765 //printf("VDeviceBUZ::new_output_buffer 1 %d\n", colormodel);
768 if(colormodel != user_frame->get_color_model())
780 user_frame = new VFrame;
783 user_frame = new VFrame(0,
792 // user_frame->set_shm_offset(0);
794 //printf("VDeviceBUZ::new_output_buffer 2\n");
798 ArrayList<int>* VDeviceBUZ::get_render_strategies()
800 return &render_strategies;
804 #endif // HAVE_VIDEO4LINUX