r972: Fix aspect ratio of YUV4MPEG streams.
[cinelerra_cv/ct.git] / cinelerra / vdevicebuz.C
blobfc38ec6410928d948e6397808234ba1d61edfa9a
1 // ALPHA C++ can't compile 64 bit headers
2 #undef _LARGEFILE_SOURCE
3 #undef _LARGEFILE64_SOURCE
4 #undef _FILE_OFFSET_BITS
6 #include "assets.h"
7 #include "bcsignals.h"
8 #include "channel.h"
9 #include "chantables.h"
10 #include "condition.h"
11 #include "file.inc"
12 #include "mutex.h"
13 #include "picture.h"
14 #include "playbackconfig.h"
15 #include "preferences.h"
16 #include "recordconfig.h"
17 #include "strategies.inc"
18 #include "vdevicebuz.h"
19 #include "vframe.h"
20 #include "videoconfig.h"
21 #include "videodevice.h"
23 #include <errno.h>
24 #include <stdint.h>
25 #include <linux/kernel.h>
26 //#include <linux/videodev2.h>
27 #include <linux/videodev.h>
28 #include <fcntl.h>
29 #include <sys/ioctl.h>
30 #include <sys/mman.h>
31 #include <unistd.h>
35 #define READ_TIMEOUT 5000000
38 VDeviceBUZInput::VDeviceBUZInput(VDeviceBUZ *device)
39  : Thread(1, 1, 0)
41         this->device = device;
42         buffer = 0;
43         buffer_size = 0;
44         total_buffers = 0;
45         current_inbuffer = 0;
46         current_outbuffer = 0;
47         done = 0;
48         output_lock = new Condition(0, "VDeviceBUZInput::output_lock");
49         buffer_lock = new Mutex("VDeviceBUZInput::buffer_lock");
52 VDeviceBUZInput::~VDeviceBUZInput()
54         if(Thread::running())
55         {
56                 done = 1;
57                 Thread::cancel();
58                 Thread::join();
59         }
61         if(buffer)
62         {
63                 for(int i = 0; i < total_buffers; i++)
64                 {
65                         delete [] buffer[i];
66                 }
67                 delete [] buffer;
68                 delete [] buffer_size;
69         }
70         delete output_lock;
71         delete buffer_lock;
74 void VDeviceBUZInput::start()
76 // Create buffers
77         total_buffers = device->device->in_config->capture_length;
78         buffer = new char*[total_buffers];
79         buffer_size = new int[total_buffers];
80         bzero(buffer_size, sizeof(int) * total_buffers);
81         for(int i = 0; i < total_buffers; i++)
82         {
83                 buffer[i] = new char[INPUT_BUFFER_SIZE];
84         }
86         Thread::start();
89 void VDeviceBUZInput::run()
91     struct buz_sync bsync;
93 // Wait for frame
94         while(1)
95         {
96                 Thread::enable_cancel();
97                 if(ioctl(device->jvideo_fd, BUZIOC_SYNC, &bsync) < 0)
98                 {
99                         perror("VDeviceBUZInput::run BUZIOC_SYNC");
100                         if(done) return;
101                         Thread::disable_cancel();
102                 }
103                 else
104                 {
105                         Thread::disable_cancel();
109                         int new_buffer = 0;
110                         buffer_lock->lock("VDeviceBUZInput::run");
111 // Save only if the current buffer is free.
112                         if(!buffer_size[current_inbuffer])
113                         {
114                                 new_buffer = 1;
115 // Copy to input buffer
116                                 memcpy(buffer[current_inbuffer], 
117                                         device->input_buffer + bsync.frame * device->breq.size,
118                                         bsync.length);
120 // Advance input buffer number and decrease semaphore.
121                                 buffer_size[current_inbuffer] = bsync.length;
122                                 increment_counter(&current_inbuffer);
123                         }
125                         buffer_lock->unlock();
127                         if(ioctl(device->jvideo_fd, BUZIOC_QBUF_CAPT, &bsync.frame))
128                                 perror("VDeviceBUZInput::run BUZIOC_QBUF_CAPT");
130                         if(new_buffer) output_lock->unlock();
131                 }
132         }
135 void VDeviceBUZInput::get_buffer(char **ptr, int *size)
137 // Increase semaphore to wait for buffer.
138         int result = output_lock->timed_lock(READ_TIMEOUT, "VDeviceBUZInput::get_buffer");
141 // The driver has its own timeout routine but it doesn't work because
142 // because the tuner lock is unlocked and relocked with no delay.
143 //      int result = 0;
144 //      output_lock->lock("VDeviceBUZInput::get_buffer");
146         if(!result)
147         {
148 // Take over buffer table
149                 buffer_lock->lock("VDeviceBUZInput::get_buffer");
150                 *ptr = buffer[current_outbuffer];
151                 *size = buffer_size[current_outbuffer];
152                 buffer_lock->unlock();
153         }
154         else
155         {
156 //printf("VDeviceBUZInput::get_buffer 1\n");
157                 output_lock->unlock();
158         }
161 void VDeviceBUZInput::put_buffer()
163         buffer_lock->lock("VDeviceBUZInput::put_buffer");
164         buffer_size[current_outbuffer] = 0;
165         buffer_lock->unlock();
166         increment_counter(&current_outbuffer);
169 void VDeviceBUZInput::increment_counter(int *counter)
171         (*counter)++;
172         if(*counter >= total_buffers) *counter = 0;
175 void VDeviceBUZInput::decrement_counter(int *counter)
177         (*counter)--;
178         if(*counter < 0) *counter = total_buffers - 1;
195 VDeviceBUZ::VDeviceBUZ(VideoDevice *device)
196  : VDeviceBase(device)
198         reset_parameters();
199         render_strategies.append(VRENDER_MJPG);
200         tuner_lock = new Mutex("VDeviceBUZ::tuner_lock");
203 VDeviceBUZ::~VDeviceBUZ()
205         close_all();
206         delete tuner_lock;
209 int VDeviceBUZ::reset_parameters()
211         jvideo_fd = 0;
212         input_buffer = 0;
213         output_buffer = 0;
214         frame_buffer = 0;
215         frame_size = 0;
216         frame_allocated = 0;
217         input_error = 0;
218         last_frame_no = 0;
219         temp_frame = 0;
220         user_frame = 0;
221         mjpeg = 0;
222         total_loops = 0;
223         output_number = 0;
224         input_thread = 0;
225         brightness = 32768;
226         hue = 32768;
227         color = 32768;
228         contrast = 32768;
229         whiteness = 32768;
232 int VDeviceBUZ::close_input_core()
234         if(input_thread)
235         {
236                 delete input_thread;
237                 input_thread = 0;
238         }
241         if(device->r)
242         {
243                 if(jvideo_fd) close(jvideo_fd);
244                 jvideo_fd = 0;
245         }
247         if(input_buffer)
248         {
249                 if(input_buffer > 0)
250                         munmap(input_buffer, breq.count * breq.size);
251                 input_buffer = 0;
252         }
255 int VDeviceBUZ::close_output_core()
257 //printf("VDeviceBUZ::close_output_core 1\n");
258         if(device->w)
259         {
260                 int n = -1;
261 //              if(ioctl(jvideo_fd, BUZIOC_QBUF_PLAY, &n) < 0)
262 //                      perror("VDeviceBUZ::close_output_core BUZIOC_QBUF_PLAY");
263                 if(jvideo_fd) close(jvideo_fd);
264                 jvideo_fd = 0;
265         }
266         if(output_buffer)
267         {
268                 if(output_buffer > 0)
269                         munmap(output_buffer, breq.count * breq.size);
270                 output_buffer = 0;
271         }
272         if(temp_frame)
273         {
274                 delete temp_frame;
275                 temp_frame = 0;
276         }
277         if(mjpeg)
278         {
279                 mjpeg_delete(mjpeg);
280                 mjpeg = 0;
281         }
282         if(user_frame)
283         {
284                 delete user_frame;
285                 user_frame = 0;
286         }
287 //printf("VDeviceBUZ::close_output_core 2\n");
288         return 0;
292 int VDeviceBUZ::close_all()
294 //printf("VDeviceBUZ::close_all 1\n");
295         close_input_core();
296 //printf("VDeviceBUZ::close_all 1\n");
297         close_output_core();
298 //printf("VDeviceBUZ::close_all 1\n");
299         if(frame_buffer) delete frame_buffer;
300 //printf("VDeviceBUZ::close_all 1\n");
301         reset_parameters();
302 //printf("VDeviceBUZ::close_all 2\n");
303         return 0;
306 #define COMPOSITE_TEXT "Composite"
307 #define SVIDEO_TEXT "S-Video"
308 #define BUZ_COMPOSITE 0
309 #define BUZ_SVIDEO 1
311 void VDeviceBUZ::get_inputs(ArrayList<Channel*> *input_sources)
313         Channel *new_source = new Channel;
315         new_source = new Channel;
316         strcpy(new_source->device_name, COMPOSITE_TEXT);
317         input_sources->append(new_source);
319         new_source = new Channel;
320         strcpy(new_source->device_name, SVIDEO_TEXT);
321         input_sources->append(new_source);
324 int VDeviceBUZ::open_input()
326         device->channel->use_norm = 1;
327         device->channel->use_input = 1;
329         device->picture->use_brightness = 1;
330         device->picture->use_contrast = 1;
331         device->picture->use_color = 1;
332         device->picture->use_hue = 1;
333         device->picture->use_whiteness = 1;
335 // Can't open input until after the channel is set
336         return 0;
339 int VDeviceBUZ::open_output()
341 // Can't open output until after the channel is set
342         return 0;
345 int VDeviceBUZ::set_channel(Channel *channel)
347         if(!channel) return 0;
349         tuner_lock->lock("VDeviceBUZ::set_channel");
351         if(device->r)
352         {
353                 close_input_core();
354                 open_input_core(channel);
355         }
356         else
357         {
358                 close_output_core();
359                 open_output_core(channel);
360         }
362         tuner_lock->unlock();
365         return 0;
368 void VDeviceBUZ::create_channeldb(ArrayList<Channel*> *channeldb)
370         ;
373 int VDeviceBUZ::set_picture(PictureConfig *picture)
375         this->brightness = (int)((float)picture->brightness / 100 * 32767 + 32768);
376         this->hue = (int)((float)picture->hue / 100 * 32767 + 32768);
377         this->color = (int)((float)picture->color / 100 * 32767 + 32768);
378         this->contrast = (int)((float)picture->contrast / 100 * 32767 + 32768);
379         this->whiteness = (int)((float)picture->whiteness / 100 * 32767 + 32768);
382         tuner_lock->lock("VDeviceBUZ::set_picture");
383         if(device->r)
384         {
385                 close_input_core();
386                 open_input_core(0);
387         }
388         else
389         {
390                 close_output_core();
391                 open_output_core(0);
392         }
393         tuner_lock->unlock();
394 // 
395 // 
396 // TRACE("VDeviceBUZ::set_picture 1");
397 //      tuner_lock->lock("VDeviceBUZ::set_picture");
398 // TRACE("VDeviceBUZ::set_picture 2");
399 // 
400 // 
401 // 
402 //      struct video_picture picture_params;
403 // // This call takes a long time in 2.4.22
404 //      if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0)
405 //              perror("VDeviceBUZ::set_picture VIDIOCGPICT");
406 //      picture_params.brightness = brightness;
407 //      picture_params.hue = hue;
408 //      picture_params.colour = color;
409 //      picture_params.contrast = contrast;
410 //      picture_params.whiteness = whiteness;
411 // // This call takes a long time in 2.4.22
412 //      if(ioctl(jvideo_fd, VIDIOCSPICT, &picture_params) < 0)
413 //              perror("VDeviceBUZ::set_picture VIDIOCSPICT");
414 //      if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0)
415 //              perror("VDeviceBUZ::set_picture VIDIOCGPICT");
416 // 
417 // 
418 // TRACE("VDeviceBUZ::set_picture 10");
419 // 
420 // 
421 //      tuner_lock->unlock();
423         return 0;
426 int VDeviceBUZ::get_norm(int norm)
428         switch(norm)
429         {
430                 case NTSC:          return VIDEO_MODE_NTSC;      break;
431                 case PAL:           return VIDEO_MODE_PAL;       break;
432                 case SECAM:         return VIDEO_MODE_SECAM;     break;
433         }
436 int VDeviceBUZ::read_buffer(VFrame *frame)
438         tuner_lock->lock("VDeviceBUZ::read_buffer");
439         if(!jvideo_fd) open_input_core(0);
441 // Get buffer from thread
442         char *buffer = 0;
443         int buffer_size = 0;
444         if(input_thread) 
445                 input_thread->get_buffer(&buffer, &buffer_size);
447         if(buffer)
448         {
449                 frame->allocate_compressed_data(buffer_size);
450                 frame->set_compressed_size(buffer_size);
452 // Transfer fields to frame
453                 if(device->odd_field_first)
454                 {
455                         long field2_offset = mjpeg_get_field2((unsigned char*)buffer, buffer_size);
456                         long field1_len = field2_offset;
457                         long field2_len = buffer_size - field2_offset;
459                         memcpy(frame->get_data(), buffer + field2_offset, field2_len);
460                         memcpy(frame->get_data() + field2_len, buffer, field1_len);
461                 }
462                 else
463                 {
464                         bcopy(buffer, frame->get_data(), buffer_size);
465                 }
467                 input_thread->put_buffer();
468                 tuner_lock->unlock();
469         }
470         else
471         {
472                 tuner_lock->unlock();
473                 Timer timer;
474 // Allow other threads to lock the tuner_lock under NPTL.
475                 timer.delay(100);
476         }
479         return 0;
482 int VDeviceBUZ::open_input_core(Channel *channel)
484         jvideo_fd = open(device->in_config->buz_in_device, O_RDONLY);
486         if(jvideo_fd <= 0)
487         {
488                 fprintf(stderr, "VDeviceBUZ::open_input %s: %s\n", 
489                         device->in_config->buz_in_device,
490                         strerror(errno));
491                 jvideo_fd = 0;
492                 return 1;
493         }
495 // Create input sources
496         get_inputs(&device->input_sources);
498 // Set current input source
499         if(channel)
500         {
501                 for(int i = 0; i < 2; i++)
502                 {
503                         struct video_channel vch;
504                         vch.channel = channel->input;
505                         vch.norm = get_norm(channel->norm);
507 //printf("VDeviceBUZ::open_input_core 2 %d %d\n", vch.channel, vch.norm);
508                         if(ioctl(jvideo_fd, VIDIOCSCHAN, &vch) < 0)
509                                 perror("VDeviceBUZ::open_input_core VIDIOCSCHAN ");
510                 }
511         }
514 // Throw away
515 //     struct video_capability vc;
516 //      if(ioctl(jvideo_fd, VIDIOCGCAP, &vc) < 0)
517 //              perror("VDeviceBUZ::open_input VIDIOCGCAP");
519 // API dependant initialization
520         if(ioctl(jvideo_fd, BUZIOC_G_PARAMS, &bparm) < 0)
521                 perror("VDeviceBUZ::open_input BUZIOC_G_PARAMS");
523         bparm.HorDcm = 1;
524         bparm.VerDcm = 1;
525         bparm.TmpDcm = 1;
526         bparm.field_per_buff = 2;
527         bparm.img_width = device->in_config->w;
528         bparm.img_height = device->in_config->h / bparm.field_per_buff;
529         bparm.img_x = 0;
530         bparm.img_y = 0;
531 //      bparm.APPn = 0;
532 //      bparm.APP_len = 14;
533         bparm.APP_len = 0;
534         bparm.odd_even = 0;
535     bparm.decimation = 0;
536     bparm.quality = device->quality;
537     bzero(bparm.APP_data, sizeof(bparm.APP_data));
539         if(ioctl(jvideo_fd, BUZIOC_S_PARAMS, &bparm) < 0)
540                 perror("VDeviceBUZ::open_input BUZIOC_S_PARAMS");
542 // printf("open_input %d %d %d %d %d %d %d %d %d %d %d %d\n", 
543 //              bparm.HorDcm,
544 //              bparm.VerDcm,
545 //              bparm.TmpDcm,
546 //              bparm.field_per_buff,
547 //              bparm.img_width,
548 //              bparm.img_height,
549 //              bparm.img_x,
550 //              bparm.img_y,
551 //              bparm.APP_len,
552 //              bparm.odd_even,
553 //      bparm.decimation,
554 //      bparm.quality);
556         breq.count = device->in_config->capture_length;
557         breq.size = INPUT_BUFFER_SIZE;
558         if(ioctl(jvideo_fd, BUZIOC_REQBUFS, &breq) < 0)
559                 perror("VDeviceBUZ::open_input BUZIOC_REQBUFS");
561 //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);
562         if((input_buffer = (char*)mmap(0, 
563                 breq.count * breq.size, 
564                 PROT_READ, 
565                 MAP_SHARED, 
566                 jvideo_fd, 
567                 0)) <= 0)
568                 perror("VDeviceBUZ::open_input mmap");
571 // Set picture quality
572         struct video_picture picture_params;
573 // This call takes a long time in 2.4.22
574         if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0)
575                 perror("VDeviceBUZ::set_picture VIDIOCGPICT");
576         picture_params.brightness = brightness;
577         picture_params.hue = hue;
578         picture_params.colour = color;
579         picture_params.contrast = contrast;
580         picture_params.whiteness = whiteness;
581 // This call takes a long time in 2.4.22
582         if(ioctl(jvideo_fd, VIDIOCSPICT, &picture_params) < 0)
583                 perror("VDeviceBUZ::set_picture VIDIOCSPICT");
584         if(ioctl(jvideo_fd, VIDIOCGPICT, &picture_params) < 0)
585                 perror("VDeviceBUZ::set_picture VIDIOCGPICT");
590 // Start capturing
591         for(int i = 0; i < breq.count; i++)
592         {
593         if(ioctl(jvideo_fd, BUZIOC_QBUF_CAPT, &i) < 0)
594                         perror("VDeviceBUZ::open_input BUZIOC_QBUF_CAPT");
595         }
598         input_thread = new VDeviceBUZInput(this);
599         input_thread->start();
600 //printf("VDeviceBUZ::open_input_core 2\n");
601         return 0;
604 int VDeviceBUZ::open_output_core(Channel *channel)
606 //printf("VDeviceBUZ::open_output 1\n");
607         total_loops = 0;
608         output_number = 0;
609         jvideo_fd = open(device->out_config->buz_out_device, O_RDWR);
610         if(jvideo_fd <= 0)
611         {
612                 perror("VDeviceBUZ::open_output");
613                 return 1;
614         }
617 // Set current input source
618         if(channel)
619         {
620                 struct video_channel vch;
621                 vch.channel = channel->input;
622                 vch.norm = get_norm(channel->norm);
624                 if(ioctl(jvideo_fd, VIDIOCSCHAN, &vch) < 0)
625                         perror("VDeviceBUZ::open_output_core VIDIOCSCHAN ");
626         }
628         breq.count = 10;
629         breq.size = INPUT_BUFFER_SIZE;
630         if(ioctl(jvideo_fd, BUZIOC_REQBUFS, &breq) < 0)
631                 perror("VDeviceBUZ::open_output BUZIOC_REQBUFS");
632         if((output_buffer = (char*)mmap(0, 
633                 breq.count * breq.size, 
634                 PROT_READ | PROT_WRITE, 
635                 MAP_SHARED, 
636                 jvideo_fd, 
637                 0)) <= 0)
638                 perror("VDeviceBUZ::open_output mmap");
640         if(ioctl(jvideo_fd, BUZIOC_G_PARAMS, &bparm) < 0)
641                 perror("VDeviceBUZ::open_output BUZIOC_G_PARAMS");
643         bparm.decimation = 1;
644         bparm.HorDcm = 1;
645         bparm.field_per_buff = 2;
646         bparm.TmpDcm = 1;
647         bparm.VerDcm = 1;
648         bparm.img_width = device->out_w;
649         bparm.img_height = device->out_h / bparm.field_per_buff;
650         bparm.img_x = 0;
651         bparm.img_y = 0;
652         bparm.odd_even = 0;
654         if(ioctl(jvideo_fd, BUZIOC_S_PARAMS, &bparm) < 0)
655                 perror("VDeviceBUZ::open_output BUZIOC_S_PARAMS");
656 //printf("VDeviceBUZ::open_output 2\n");
657         return 0;
662 int VDeviceBUZ::write_buffer(VFrame *frame, EDL *edl)
664 //printf("VDeviceBUZ::write_buffer 1\n");
665         tuner_lock->lock("VDeviceBUZ::write_buffer");
667         if(!jvideo_fd) open_output_core(0);
669         VFrame *ptr = 0;
670         if(frame->get_color_model() != BC_COMPRESSED)
671         {
672                 if(!temp_frame) temp_frame = new VFrame;
673                 if(!mjpeg)
674                 {
675                         mjpeg = mjpeg_new(device->out_w, device->out_h, 2);
676                         mjpeg_set_quality(mjpeg, device->quality);
677                         mjpeg_set_float(mjpeg, 0);
678                 }
679                 ptr = temp_frame;
680                 mjpeg_compress(mjpeg, 
681                         frame->get_rows(), 
682                         frame->get_y(), 
683                         frame->get_u(), 
684                         frame->get_v(),
685                         frame->get_color_model(),
686                         device->cpus);
687                 temp_frame->allocate_compressed_data(mjpeg_output_size(mjpeg));
688                 temp_frame->set_compressed_size(mjpeg_output_size(mjpeg));
689                 bcopy(mjpeg_output_buffer(mjpeg), temp_frame->get_data(), mjpeg_output_size(mjpeg));
690         }
691         else
692                 ptr = frame;
694 // Wait for frame to become available
695 // Caused close_output_core to lock up.
696 //      if(total_loops >= 1)
697 //      {
698 //              if(ioctl(jvideo_fd, BUZIOC_SYNC, &output_number) < 0)
699 //                      perror("VDeviceBUZ::write_buffer BUZIOC_SYNC");
700 //      }
702         if(device->out_config->buz_swap_fields)
703         {
704                 long field2_offset = mjpeg_get_field2((unsigned char*)ptr->get_data(), 
705                         ptr->get_compressed_size());
706                 long field2_len = ptr->get_compressed_size() - field2_offset;
707                 memcpy(output_buffer + output_number * breq.size, 
708                         ptr->get_data() + field2_offset, 
709                         field2_len);
710                 memcpy(output_buffer + output_number * breq.size +field2_len,
711                         ptr->get_data(), 
712                         field2_offset);
713         }
714         else
715         {
716                 bcopy(ptr->get_data(), 
717                         output_buffer + output_number * breq.size, 
718                         ptr->get_compressed_size());
719         }
721         if(ioctl(jvideo_fd, BUZIOC_QBUF_PLAY, &output_number) < 0)
722                 perror("VDeviceBUZ::write_buffer BUZIOC_QBUF_PLAY");
724         output_number++;
725         if(output_number >= breq.count)
726         {
727                 output_number = 0;
728                 total_loops++;
729         }
730         tuner_lock->unlock();
731 //printf("VDeviceBUZ::write_buffer 2\n");
733         return 0;
736 void VDeviceBUZ::new_output_buffer(VFrame *output,
737         int colormodel)
739 //printf("VDeviceBUZ::new_output_buffer 1 %d\n", colormodel);
740         if(user_frame)
741         {
742                 if(colormodel != user_frame->get_color_model())
743                 {
744                         delete user_frame;
745                         user_frame = 0;
746                 }
747         }
749         if(!user_frame)
750         {
751                 switch(colormodel)
752                 {
753                         case BC_COMPRESSED:
754                                 user_frame = new VFrame;
755                                 break;
756                         default:
757                                 user_frame = new VFrame(0,
758                                         device->out_w,
759                                         device->out_h,
760                                         colormodel,
761                                         -1);
762                                 break;
763                 }
764         }
765         user_frame->set_shm_offset(0);
766         output = user_frame;
767 //printf("VDeviceBUZ::new_output_buffer 2\n");
771 ArrayList<int>* VDeviceBUZ::get_render_strategies()
773         return &render_strategies;