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"
45 #include "videoconfig.h"
46 #include "videodevice.h"
51 #include <linux/kernel.h>
53 #include <sys/ioctl.h>
57 #define BASE_VIDIOCPRIVATE 192
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 void VDeviceBUZ::reset_parameters()
257 void 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 strcpy(new_source->device_name, COMPOSITE_TEXT);
341 input_sources->append(new_source);
343 new_source = new Channel;
344 strcpy(new_source->device_name, SVIDEO_TEXT);
345 input_sources->append(new_source);
348 int VDeviceBUZ::open_input()
350 device->channel->use_norm = 1;
351 device->channel->use_input = 1;
353 device->picture->use_brightness = 1;
354 device->picture->use_contrast = 1;
355 device->picture->use_color = 1;
356 device->picture->use_hue = 1;
357 device->picture->use_whiteness = 1;
359 // Can't open input until after the channel is set
363 int VDeviceBUZ::open_output()
365 // Can't open output until after the channel is set
369 int VDeviceBUZ::set_channel(Channel *channel)
371 if(!channel) return 0;
373 tuner_lock->lock("VDeviceBUZ::set_channel");
378 open_input_core(channel);
383 open_output_core(channel);
386 tuner_lock->unlock();
392 int VDeviceBUZ::create_channeldb(ArrayList<Channel*> *channeldb)
397 int VDeviceBUZ::set_picture(PictureConfig *picture)
399 this->brightness = (int)((float)picture->brightness / 100 * 32767 + 32768);
400 this->hue = (int)((float)picture->hue / 100 * 32767 + 32768);
401 this->color = (int)((float)picture->color / 100 * 32767 + 32768);
402 this->contrast = (int)((float)picture->contrast / 100 * 32767 + 32768);
403 this->whiteness = (int)((float)picture->whiteness / 100 * 32767 + 32768);
406 tuner_lock->lock("VDeviceBUZ::set_picture");
417 tuner_lock->unlock();
420 // TRACE("VDeviceBUZ::set_picture 1");
421 // tuner_lock->lock("VDeviceBUZ::set_picture");
422 // TRACE("VDeviceBUZ::set_picture 2");
426 // struct video_picture picture_params;
427 // // This call takes a long time in 2.4.22
428 // if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0)
429 // perror("VDeviceBUZ::set_picture VIDIOCGPICT");
430 // picture_params.brightness = brightness;
431 // picture_params.hue = hue;
432 // picture_params.colour = color;
433 // picture_params.contrast = contrast;
434 // picture_params.whiteness = whiteness;
435 // // This call takes a long time in 2.4.22
436 // if(ioctl(jvideo_fd, VIDIOCSPICT, &picture_params) < 0)
437 // perror("VDeviceBUZ::set_picture VIDIOCSPICT");
438 // if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0)
439 // perror("VDeviceBUZ::set_picture VIDIOCGPICT");
442 // TRACE("VDeviceBUZ::set_picture 10");
445 // tuner_lock->unlock();
450 int VDeviceBUZ::get_norm(int norm)
454 case NTSC: return VIDEO_MODE_NTSC;
455 case PAL: return VIDEO_MODE_PAL;
456 case SECAM: return VIDEO_MODE_SECAM;
458 printf("VDeviceBUZ::get_norm: unknown norm %d\n", norm);
459 return VIDEO_MODE_NTSC;
462 int VDeviceBUZ::read_buffer(VFrame *frame)
464 tuner_lock->lock("VDeviceBUZ::read_buffer");
465 if(!jvideo_fd) open_input_core(0);
467 // Get buffer from thread
471 input_thread->get_buffer(&buffer, &buffer_size);
475 frame->allocate_compressed_data(buffer_size);
476 frame->set_compressed_size(buffer_size);
478 // Transfer fields to frame
479 if(device->odd_field_first)
481 long field2_offset = mjpeg_get_field2((unsigned char*)buffer, buffer_size);
482 long field1_len = field2_offset;
483 long field2_len = buffer_size - field2_offset;
485 memcpy(frame->get_data(), buffer + field2_offset, field2_len);
486 memcpy(frame->get_data() + field2_len, buffer, field1_len);
490 bcopy(buffer, frame->get_data(), buffer_size);
493 input_thread->put_buffer();
494 tuner_lock->unlock();
498 tuner_lock->unlock();
500 // Allow other threads to lock the tuner_lock under NPTL.
508 int VDeviceBUZ::open_input_core(Channel *channel)
510 jvideo_fd = open(device->in_config->buz_in_device, O_RDONLY);
514 fprintf(stderr, "VDeviceBUZ::open_input %s: %s\n",
515 device->in_config->buz_in_device,
521 // Create input sources
522 get_inputs(&device->input_sources);
524 // Set current input source
527 for(int i = 0; i < 2; i++)
529 struct video_channel vch;
530 vch.channel = channel->input;
531 vch.norm = get_norm(channel->norm);
533 //printf("VDeviceBUZ::open_input_core 2 %d %d\n", vch.channel, vch.norm);
534 if(ioctl(jvideo_fd, VIDIOCSCHAN, &vch) < 0)
535 perror("VDeviceBUZ::open_input_core VIDIOCSCHAN ");
541 // struct video_capability vc;
542 // if(ioctl(jvideo_fd, VIDIOCGCAP, &vc) < 0)
543 // perror("VDeviceBUZ::open_input VIDIOCGCAP");
545 // API dependant initialization
546 if(ioctl(jvideo_fd, BUZIOC_G_PARAMS, &bparm) < 0)
547 perror("VDeviceBUZ::open_input BUZIOC_G_PARAMS");
552 bparm.field_per_buff = 2;
553 bparm.img_width = device->in_config->w;
554 bparm.img_height = device->in_config->h / bparm.field_per_buff;
558 // bparm.APP_len = 14;
561 bparm.decimation = 0;
562 bparm.quality = device->quality;
563 bzero(bparm.APP_data, sizeof(bparm.APP_data));
565 if(ioctl(jvideo_fd, BUZIOC_S_PARAMS, &bparm) < 0)
566 perror("VDeviceBUZ::open_input BUZIOC_S_PARAMS");
568 // printf("open_input %d %d %d %d %d %d %d %d %d %d %d %d\n",
572 // bparm.field_per_buff,
582 breq.count = device->in_config->capture_length;
583 breq.size = INPUT_BUFFER_SIZE;
584 if(ioctl(jvideo_fd, BUZIOC_REQBUFS, &breq) < 0)
585 perror("VDeviceBUZ::open_input BUZIOC_REQBUFS");
587 //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);
588 if((input_buffer = (char*)mmap(0,
589 breq.count * breq.size,
594 perror("VDeviceBUZ::open_input mmap");
597 // Set picture quality
598 struct video_picture picture_params;
599 // This call takes a long time in 2.4.22
600 if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0)
601 perror("VDeviceBUZ::set_picture VIDIOCGPICT");
602 picture_params.brightness = brightness;
603 picture_params.hue = hue;
604 picture_params.colour = color;
605 picture_params.contrast = contrast;
606 picture_params.whiteness = whiteness;
607 // This call takes a long time in 2.4.22
608 if(ioctl(jvideo_fd, VIDIOCSPICT, &picture_params) < 0)
609 perror("VDeviceBUZ::set_picture VIDIOCSPICT");
610 if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0)
611 perror("VDeviceBUZ::set_picture VIDIOCGPICT");
615 int count = breq.count;
616 for(int i = 0; i < count; i++)
618 if(ioctl(jvideo_fd, BUZIOC_QBUF_CAPT, &i) < 0)
619 perror("VDeviceBUZ::open_input BUZIOC_QBUF_CAPT");
623 input_thread = new VDeviceBUZInput(this);
624 input_thread->start();
625 //printf("VDeviceBUZ::open_input_core 2\n");
629 int VDeviceBUZ::open_output_core(Channel *channel)
631 //printf("VDeviceBUZ::open_output 1\n");
634 jvideo_fd = open(device->out_config->buz_out_device, O_RDWR);
637 perror("VDeviceBUZ::open_output");
642 // Set current input source
645 struct video_channel vch;
646 vch.channel = channel->input;
647 vch.norm = get_norm(channel->norm);
649 if(ioctl(jvideo_fd, VIDIOCSCHAN, &vch) < 0)
650 perror("VDeviceBUZ::open_output_core VIDIOCSCHAN ");
654 breq.size = INPUT_BUFFER_SIZE;
655 if(ioctl(jvideo_fd, BUZIOC_REQBUFS, &breq) < 0)
656 perror("VDeviceBUZ::open_output BUZIOC_REQBUFS");
657 if((output_buffer = (char*)mmap(0,
658 breq.count * breq.size,
659 PROT_READ | PROT_WRITE,
663 perror("VDeviceBUZ::open_output mmap");
665 if(ioctl(jvideo_fd, BUZIOC_G_PARAMS, &bparm) < 0)
666 perror("VDeviceBUZ::open_output BUZIOC_G_PARAMS");
668 bparm.decimation = 1;
670 bparm.field_per_buff = 2;
673 bparm.img_width = device->out_w;
674 bparm.img_height = device->out_h / bparm.field_per_buff;
679 if(ioctl(jvideo_fd, BUZIOC_S_PARAMS, &bparm) < 0)
680 perror("VDeviceBUZ::open_output BUZIOC_S_PARAMS");
681 //printf("VDeviceBUZ::open_output 2\n");
687 int VDeviceBUZ::write_buffer(VFrame *frame, EDL *edl)
689 //printf("VDeviceBUZ::write_buffer 1\n");
690 tuner_lock->lock("VDeviceBUZ::write_buffer");
692 if(!jvideo_fd) open_output_core(0);
695 if(frame->get_color_model() != BC_COMPRESSED)
697 if(!temp_frame) temp_frame = new VFrame;
700 mjpeg = mjpeg_new(device->out_w, device->out_h, 2);
701 mjpeg_set_quality(mjpeg, device->quality);
702 mjpeg_set_float(mjpeg, 0);
705 mjpeg_compress(mjpeg,
710 frame->get_color_model(),
712 temp_frame->allocate_compressed_data(mjpeg_output_size(mjpeg));
713 temp_frame->set_compressed_size(mjpeg_output_size(mjpeg));
714 bcopy(mjpeg_output_buffer(mjpeg), temp_frame->get_data(), mjpeg_output_size(mjpeg));
719 // Wait for frame to become available
720 // Caused close_output_core to lock up.
721 // if(total_loops >= 1)
723 // if(ioctl(jvideo_fd, BUZIOC_SYNC, &output_number) < 0)
724 // perror("VDeviceBUZ::write_buffer BUZIOC_SYNC");
727 if(device->out_config->buz_swap_fields)
729 long field2_offset = mjpeg_get_field2((unsigned char*)ptr->get_data(),
730 ptr->get_compressed_size());
731 long field2_len = ptr->get_compressed_size() - field2_offset;
732 memcpy(output_buffer + output_number * breq.size,
733 ptr->get_data() + field2_offset,
735 memcpy(output_buffer + output_number * breq.size +field2_len,
741 bcopy(ptr->get_data(),
742 output_buffer + output_number * breq.size,
743 ptr->get_compressed_size());
746 if(ioctl(jvideo_fd, BUZIOC_QBUF_PLAY, &output_number) < 0)
747 perror("VDeviceBUZ::write_buffer BUZIOC_QBUF_PLAY");
750 if(output_number >= (int)breq.count)
755 tuner_lock->unlock();
756 //printf("VDeviceBUZ::write_buffer 2\n");
761 void VDeviceBUZ::new_output_buffer(VFrame *output,
764 //printf("VDeviceBUZ::new_output_buffer 1 %d\n", colormodel);
767 if(colormodel != user_frame->get_color_model())
779 user_frame = new VFrame;
782 user_frame = new VFrame(0,
791 // user_frame->set_shm_offset(0);
793 //printf("VDeviceBUZ::new_output_buffer 2\n");
797 ArrayList<int>* VDeviceBUZ::get_render_strategies()
799 return &render_strategies;
803 #endif // HAVE_VIDEO4LINUX