; /* * CINELERRA * Copyright (C) 2008 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifdef HAVE_VIDEO4LINUX // ALPHA C++ can't compile 64 bit headers #undef _LARGEFILE_SOURCE #undef _LARGEFILE64_SOURCE #undef _FILE_OFFSET_BITS #include "assets.h" #include "bcsignals.h" #include "channel.h" #include "chantables.h" #include "condition.h" #include "file.inc" #include "mutex.h" #include "picture.h" #include "playbackconfig.h" #include "preferences.h" #include "recordconfig.h" #include "strategies.inc" #include "vdevicebuz.h" #include "videodev.h" #include "vframe.h" #include "videoconfig.h" #include "videodevice.h" #include #include #include #include #include #include #include #include #define BASE_VIDIOCPRIVATE 192 #define READ_TIMEOUT 5000000 VDeviceBUZInput::VDeviceBUZInput(VDeviceBUZ *device) : Thread(1, 1, 0) { this->device = device; buffer = 0; buffer_size = 0; total_buffers = 0; current_inbuffer = 0; current_outbuffer = 0; done = 0; output_lock = new Condition(0, "VDeviceBUZInput::output_lock"); buffer_lock = new Mutex("VDeviceBUZInput::buffer_lock"); } VDeviceBUZInput::~VDeviceBUZInput() { if(Thread::running()) { done = 1; Thread::cancel(); } Thread::join(); if(buffer) { for(int i = 0; i < total_buffers; i++) { delete [] buffer[i]; } delete [] buffer; delete [] buffer_size; } delete output_lock; delete buffer_lock; } void VDeviceBUZInput::start() { // Create buffers total_buffers = device->device->in_config->capture_length; buffer = new char*[total_buffers]; buffer_size = new int[total_buffers]; bzero(buffer_size, sizeof(int) * total_buffers); for(int i = 0; i < total_buffers; i++) { buffer[i] = new char[INPUT_BUFFER_SIZE]; } Thread::start(); } void VDeviceBUZInput::run() { struct buz_sync bsync; // Wait for frame while(1) { Thread::enable_cancel(); if(ioctl(device->jvideo_fd, BUZIOC_SYNC, &bsync) < 0) { perror("VDeviceBUZInput::run BUZIOC_SYNC"); if(done) return; Thread::disable_cancel(); } else { Thread::disable_cancel(); int new_buffer = 0; buffer_lock->lock("VDeviceBUZInput::run"); // Save only if the current buffer is free. if(!buffer_size[current_inbuffer]) { new_buffer = 1; // Copy to input buffer memcpy(buffer[current_inbuffer], device->input_buffer + bsync.frame * device->breq.size, bsync.length); // Advance input buffer number and decrease semaphore. buffer_size[current_inbuffer] = bsync.length; increment_counter(¤t_inbuffer); } buffer_lock->unlock(); if(ioctl(device->jvideo_fd, BUZIOC_QBUF_CAPT, &bsync.frame)) perror("VDeviceBUZInput::run BUZIOC_QBUF_CAPT"); if(new_buffer) output_lock->unlock(); } } } void VDeviceBUZInput::get_buffer(char **ptr, int *size) { // Increase semaphore to wait for buffer. int result = output_lock->timed_lock(READ_TIMEOUT, "VDeviceBUZInput::get_buffer"); // The driver has its own timeout routine but it doesn't work because // because the tuner lock is unlocked and relocked with no delay. // int result = 0; // output_lock->lock("VDeviceBUZInput::get_buffer"); if(!result) { // Take over buffer table buffer_lock->lock("VDeviceBUZInput::get_buffer"); *ptr = buffer[current_outbuffer]; *size = buffer_size[current_outbuffer]; buffer_lock->unlock(); } else { //printf("VDeviceBUZInput::get_buffer 1\n"); output_lock->unlock(); } } void VDeviceBUZInput::put_buffer() { buffer_lock->lock("VDeviceBUZInput::put_buffer"); buffer_size[current_outbuffer] = 0; buffer_lock->unlock(); increment_counter(¤t_outbuffer); } void VDeviceBUZInput::increment_counter(int *counter) { (*counter)++; if(*counter >= total_buffers) *counter = 0; } void VDeviceBUZInput::decrement_counter(int *counter) { (*counter)--; if(*counter < 0) *counter = total_buffers - 1; } VDeviceBUZ::VDeviceBUZ(VideoDevice *device) : VDeviceBase(device) { reset_parameters(); render_strategies.append(VRENDER_MJPG); tuner_lock = new Mutex("VDeviceBUZ::tuner_lock"); } VDeviceBUZ::~VDeviceBUZ() { close_all(); delete tuner_lock; } void VDeviceBUZ::reset_parameters() { jvideo_fd = 0; input_buffer = 0; output_buffer = 0; frame_buffer = 0; frame_size = 0; frame_allocated = 0; input_error = 0; last_frame_no = 0; temp_frame = 0; user_frame = 0; mjpeg = 0; total_loops = 0; output_number = 0; input_thread = 0; brightness = 32768; hue = 32768; color = 32768; contrast = 32768; whiteness = 32768; return 0; } void VDeviceBUZ::close_input_core() { if(input_thread) { delete input_thread; input_thread = 0; } if(device->r) { if(jvideo_fd) close(jvideo_fd); jvideo_fd = 0; } if(input_buffer) { if(input_buffer > 0) munmap(input_buffer, breq.count * breq.size); input_buffer = 0; } return 0; } int VDeviceBUZ::close_output_core() { //printf("VDeviceBUZ::close_output_core 1\n"); if(device->w) { // if(ioctl(jvideo_fd, BUZIOC_QBUF_PLAY, &n) < 0) // perror("VDeviceBUZ::close_output_core BUZIOC_QBUF_PLAY"); if(jvideo_fd) close(jvideo_fd); jvideo_fd = 0; } if(output_buffer) { if(output_buffer > 0) munmap(output_buffer, breq.count * breq.size); output_buffer = 0; } if(temp_frame) { delete temp_frame; temp_frame = 0; } if(mjpeg) { mjpeg_delete(mjpeg); mjpeg = 0; } if(user_frame) { delete user_frame; user_frame = 0; } //printf("VDeviceBUZ::close_output_core 2\n"); return 0; } int VDeviceBUZ::close_all() { //printf("VDeviceBUZ::close_all 1\n"); close_input_core(); //printf("VDeviceBUZ::close_all 1\n"); close_output_core(); //printf("VDeviceBUZ::close_all 1\n"); if(frame_buffer) delete frame_buffer; //printf("VDeviceBUZ::close_all 1\n"); reset_parameters(); //printf("VDeviceBUZ::close_all 2\n"); return 0; } #define COMPOSITE_TEXT "Composite" #define SVIDEO_TEXT "S-Video" #define BUZ_COMPOSITE 0 #define BUZ_SVIDEO 1 void VDeviceBUZ::get_inputs(ArrayList *input_sources) { Channel *new_source = new Channel; strcpy(new_source->device_name, COMPOSITE_TEXT); input_sources->append(new_source); new_source = new Channel; strcpy(new_source->device_name, SVIDEO_TEXT); input_sources->append(new_source); } int VDeviceBUZ::open_input() { device->channel->use_norm = 1; device->channel->use_input = 1; device->picture->use_brightness = 1; device->picture->use_contrast = 1; device->picture->use_color = 1; device->picture->use_hue = 1; device->picture->use_whiteness = 1; // Can't open input until after the channel is set return 0; } int VDeviceBUZ::open_output() { // Can't open output until after the channel is set return 0; } int VDeviceBUZ::set_channel(Channel *channel) { if(!channel) return 0; tuner_lock->lock("VDeviceBUZ::set_channel"); if(device->r) { close_input_core(); open_input_core(channel); } else { close_output_core(); open_output_core(channel); } tuner_lock->unlock(); return 0; } int VDeviceBUZ::create_channeldb(ArrayList *channeldb) { return 0; } int VDeviceBUZ::set_picture(PictureConfig *picture) { this->brightness = (int)((float)picture->brightness / 100 * 32767 + 32768); this->hue = (int)((float)picture->hue / 100 * 32767 + 32768); this->color = (int)((float)picture->color / 100 * 32767 + 32768); this->contrast = (int)((float)picture->contrast / 100 * 32767 + 32768); this->whiteness = (int)((float)picture->whiteness / 100 * 32767 + 32768); tuner_lock->lock("VDeviceBUZ::set_picture"); if(device->r) { close_input_core(); open_input_core(0); } else { close_output_core(); open_output_core(0); } tuner_lock->unlock(); // // // TRACE("VDeviceBUZ::set_picture 1"); // tuner_lock->lock("VDeviceBUZ::set_picture"); // TRACE("VDeviceBUZ::set_picture 2"); // // // // struct video_picture picture_params; // // This call takes a long time in 2.4.22 // if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0) // perror("VDeviceBUZ::set_picture VIDIOCGPICT"); // picture_params.brightness = brightness; // picture_params.hue = hue; // picture_params.colour = color; // picture_params.contrast = contrast; // picture_params.whiteness = whiteness; // // This call takes a long time in 2.4.22 // if(ioctl(jvideo_fd, VIDIOCSPICT, &picture_params) < 0) // perror("VDeviceBUZ::set_picture VIDIOCSPICT"); // if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0) // perror("VDeviceBUZ::set_picture VIDIOCGPICT"); // // // TRACE("VDeviceBUZ::set_picture 10"); // // // tuner_lock->unlock(); return 0; } int VDeviceBUZ::get_norm(int norm) { switch(norm) { case NTSC: return VIDEO_MODE_NTSC; case PAL: return VIDEO_MODE_PAL; case SECAM: return VIDEO_MODE_SECAM; } printf("VDeviceBUZ::get_norm: unknown norm %d\n", norm); return VIDEO_MODE_NTSC; } int VDeviceBUZ::read_buffer(VFrame *frame) { tuner_lock->lock("VDeviceBUZ::read_buffer"); if(!jvideo_fd) open_input_core(0); // Get buffer from thread char *buffer = 0; int buffer_size = 0; if(input_thread) input_thread->get_buffer(&buffer, &buffer_size); if(buffer) { frame->allocate_compressed_data(buffer_size); frame->set_compressed_size(buffer_size); // Transfer fields to frame if(device->odd_field_first) { long field2_offset = mjpeg_get_field2((unsigned char*)buffer, buffer_size); long field1_len = field2_offset; long field2_len = buffer_size - field2_offset; memcpy(frame->get_data(), buffer + field2_offset, field2_len); memcpy(frame->get_data() + field2_len, buffer, field1_len); } else { bcopy(buffer, frame->get_data(), buffer_size); } input_thread->put_buffer(); tuner_lock->unlock(); } else { tuner_lock->unlock(); Timer timer; // Allow other threads to lock the tuner_lock under NPTL. timer.delay(100); } return 0; } int VDeviceBUZ::open_input_core(Channel *channel) { jvideo_fd = open(device->in_config->buz_in_device, O_RDONLY); if(jvideo_fd <= 0) { fprintf(stderr, "VDeviceBUZ::open_input %s: %s\n", device->in_config->buz_in_device, strerror(errno)); jvideo_fd = 0; return 1; } // Create input sources get_inputs(&device->input_sources); // Set current input source if(channel) { for(int i = 0; i < 2; i++) { struct video_channel vch; vch.channel = channel->input; vch.norm = get_norm(channel->norm); //printf("VDeviceBUZ::open_input_core 2 %d %d\n", vch.channel, vch.norm); if(ioctl(jvideo_fd, VIDIOCSCHAN, &vch) < 0) perror("VDeviceBUZ::open_input_core VIDIOCSCHAN "); } } // Throw away // struct video_capability vc; // if(ioctl(jvideo_fd, VIDIOCGCAP, &vc) < 0) // perror("VDeviceBUZ::open_input VIDIOCGCAP"); // API dependant initialization if(ioctl(jvideo_fd, BUZIOC_G_PARAMS, &bparm) < 0) perror("VDeviceBUZ::open_input BUZIOC_G_PARAMS"); bparm.HorDcm = 1; bparm.VerDcm = 1; bparm.TmpDcm = 1; bparm.field_per_buff = 2; bparm.img_width = device->in_config->w; bparm.img_height = device->in_config->h / bparm.field_per_buff; bparm.img_x = 0; bparm.img_y = 0; // bparm.APPn = 0; // bparm.APP_len = 14; bparm.APP_len = 0; bparm.odd_even = 0; bparm.decimation = 0; bparm.quality = device->quality; bzero(bparm.APP_data, sizeof(bparm.APP_data)); if(ioctl(jvideo_fd, BUZIOC_S_PARAMS, &bparm) < 0) perror("VDeviceBUZ::open_input BUZIOC_S_PARAMS"); // printf("open_input %d %d %d %d %d %d %d %d %d %d %d %d\n", // bparm.HorDcm, // bparm.VerDcm, // bparm.TmpDcm, // bparm.field_per_buff, // bparm.img_width, // bparm.img_height, // bparm.img_x, // bparm.img_y, // bparm.APP_len, // bparm.odd_even, // bparm.decimation, // bparm.quality); breq.count = device->in_config->capture_length; breq.size = INPUT_BUFFER_SIZE; if(ioctl(jvideo_fd, BUZIOC_REQBUFS, &breq) < 0) perror("VDeviceBUZ::open_input BUZIOC_REQBUFS"); //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); if((input_buffer = (char*)mmap(0, breq.count * breq.size, PROT_READ, MAP_SHARED, jvideo_fd, 0)) == MAP_FAILED) perror("VDeviceBUZ::open_input mmap"); // Set picture quality struct video_picture picture_params; // This call takes a long time in 2.4.22 if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0) perror("VDeviceBUZ::set_picture VIDIOCGPICT"); picture_params.brightness = brightness; picture_params.hue = hue; picture_params.colour = color; picture_params.contrast = contrast; picture_params.whiteness = whiteness; // This call takes a long time in 2.4.22 if(ioctl(jvideo_fd, VIDIOCSPICT, &picture_params) < 0) perror("VDeviceBUZ::set_picture VIDIOCSPICT"); if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0) perror("VDeviceBUZ::set_picture VIDIOCGPICT"); // Start capturing int count = breq.count; for(int i = 0; i < count; i++) { if(ioctl(jvideo_fd, BUZIOC_QBUF_CAPT, &i) < 0) perror("VDeviceBUZ::open_input BUZIOC_QBUF_CAPT"); } input_thread = new VDeviceBUZInput(this); input_thread->start(); //printf("VDeviceBUZ::open_input_core 2\n"); return 0; } int VDeviceBUZ::open_output_core(Channel *channel) { //printf("VDeviceBUZ::open_output 1\n"); total_loops = 0; output_number = 0; jvideo_fd = open(device->out_config->buz_out_device, O_RDWR); if(jvideo_fd <= 0) { perror("VDeviceBUZ::open_output"); return 1; } // Set current input source if(channel) { struct video_channel vch; vch.channel = channel->input; vch.norm = get_norm(channel->norm); if(ioctl(jvideo_fd, VIDIOCSCHAN, &vch) < 0) perror("VDeviceBUZ::open_output_core VIDIOCSCHAN "); } breq.count = 10; breq.size = INPUT_BUFFER_SIZE; if(ioctl(jvideo_fd, BUZIOC_REQBUFS, &breq) < 0) perror("VDeviceBUZ::open_output BUZIOC_REQBUFS"); if((output_buffer = (char*)mmap(0, breq.count * breq.size, PROT_READ | PROT_WRITE, MAP_SHARED, jvideo_fd, 0)) == MAP_FAILED) perror("VDeviceBUZ::open_output mmap"); if(ioctl(jvideo_fd, BUZIOC_G_PARAMS, &bparm) < 0) perror("VDeviceBUZ::open_output BUZIOC_G_PARAMS"); bparm.decimation = 1; bparm.HorDcm = 1; bparm.field_per_buff = 2; bparm.TmpDcm = 1; bparm.VerDcm = 1; bparm.img_width = device->out_w; bparm.img_height = device->out_h / bparm.field_per_buff; bparm.img_x = 0; bparm.img_y = 0; bparm.odd_even = 0; if(ioctl(jvideo_fd, BUZIOC_S_PARAMS, &bparm) < 0) perror("VDeviceBUZ::open_output BUZIOC_S_PARAMS"); //printf("VDeviceBUZ::open_output 2\n"); return 0; } int VDeviceBUZ::write_buffer(VFrame *frame, EDL *edl) { //printf("VDeviceBUZ::write_buffer 1\n"); tuner_lock->lock("VDeviceBUZ::write_buffer"); if(!jvideo_fd) open_output_core(0); VFrame *ptr = 0; if(frame->get_color_model() != BC_COMPRESSED) { if(!temp_frame) temp_frame = new VFrame; if(!mjpeg) { mjpeg = mjpeg_new(device->out_w, device->out_h, 2); mjpeg_set_quality(mjpeg, device->quality); mjpeg_set_float(mjpeg, 0); } ptr = temp_frame; mjpeg_compress(mjpeg, frame->get_rows(), frame->get_y(), frame->get_u(), frame->get_v(), frame->get_color_model(), device->cpus); temp_frame->allocate_compressed_data(mjpeg_output_size(mjpeg)); temp_frame->set_compressed_size(mjpeg_output_size(mjpeg)); bcopy(mjpeg_output_buffer(mjpeg), temp_frame->get_data(), mjpeg_output_size(mjpeg)); } else ptr = frame; // Wait for frame to become available // Caused close_output_core to lock up. // if(total_loops >= 1) // { // if(ioctl(jvideo_fd, BUZIOC_SYNC, &output_number) < 0) // perror("VDeviceBUZ::write_buffer BUZIOC_SYNC"); // } if(device->out_config->buz_swap_fields) { long field2_offset = mjpeg_get_field2((unsigned char*)ptr->get_data(), ptr->get_compressed_size()); long field2_len = ptr->get_compressed_size() - field2_offset; memcpy(output_buffer + output_number * breq.size, ptr->get_data() + field2_offset, field2_len); memcpy(output_buffer + output_number * breq.size +field2_len, ptr->get_data(), field2_offset); } else { bcopy(ptr->get_data(), output_buffer + output_number * breq.size, ptr->get_compressed_size()); } if(ioctl(jvideo_fd, BUZIOC_QBUF_PLAY, &output_number) < 0) perror("VDeviceBUZ::write_buffer BUZIOC_QBUF_PLAY"); output_number++; if(output_number >= (int)breq.count) { output_number = 0; total_loops++; } tuner_lock->unlock(); //printf("VDeviceBUZ::write_buffer 2\n"); return 0; } void VDeviceBUZ::new_output_buffer(VFrame *output, int colormodel) { //printf("VDeviceBUZ::new_output_buffer 1 %d\n", colormodel); if(user_frame) { if(colormodel != user_frame->get_color_model()) { delete user_frame; user_frame = 0; } } if(!user_frame) { switch(colormodel) { case BC_COMPRESSED: user_frame = new VFrame; break; default: user_frame = new VFrame(0, -1, device->out_w, device->out_h, colormodel, -1); break; } } // user_frame->set_shm_offset(0); output = user_frame; //printf("VDeviceBUZ::new_output_buffer 2\n"); } ArrayList* VDeviceBUZ::get_render_strategies() { return &render_strategies; } #endif // HAVE_VIDEO4LINUX