Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / cinelerra / vdevicelml.C
diff --git a/cinelerra-5.1/cinelerra/vdevicelml.C b/cinelerra-5.1/cinelerra/vdevicelml.C
new file mode 100644 (file)
index 0000000..d200b7b
--- /dev/null
@@ -0,0 +1,439 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
+ * 
+ * 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
+ * 
+ */
+
+#include "assets.h"
+#include "file.inc"
+#include "language.h"
+#include "playbackconfig.h"
+#include "preferences.h"
+#include "recordconfig.h"
+#include "strategies.inc"
+#include "vdevicelml.h"
+#include "vframe.h"
+#include "videoconfig.h"
+#include "videodevice.h"
+
+
+#define SOI 0xffd8
+#define APP3 0xffe3
+#define APP1 0xffe1
+#define APP0 0xffe0
+#define EOI  0xffd9
+
+VDeviceLML::VDeviceLML(VideoDevice *device)
+ : VDeviceBase(device)
+{
+       reset_parameters();
+       render_strategies.append(VRENDER_MJPG);
+}
+
+VDeviceLML::~VDeviceLML()
+{
+       close_all();
+}
+
+int VDeviceLML::reset_parameters()
+{
+       jvideo_fd = 0;
+       input_buffer = 0;
+       frame_buffer = 0;
+       frame_size = 0;
+       frame_allocated = 0;
+       input_error = 0;
+       input_position = INPUT_BUFFER_SIZE;
+       last_frame_no = 0;
+}
+
+int VDeviceLML::open_input()
+{
+       jvideo_fd = fopen(device->in_config->lml_in_device, "rb");
+       if(jvideo_fd)
+       {
+               return 0;
+       }
+       else
+       {
+               perror("VDeviceLML::open_input");
+               jvideo_fd = 0;
+               return 1;
+       }
+       return 0;
+}
+
+int VDeviceLML::open_output()
+{
+       jvideo_fd = fopen(device->out_config->lml_out_device, "wb");
+       if(jvideo_fd)
+       {
+               return 0;
+       }
+       else
+       {
+               perror("VDeviceLML::open_output");
+               jvideo_fd = 0;
+               return 1;
+       }
+       return 0;
+}
+
+int VDeviceLML::close_all()
+{
+       if(device->r)
+       {
+               if(jvideo_fd) fclose(jvideo_fd);
+       }
+       if(device->w)
+       {
+               if(jvideo_fd) fclose(jvideo_fd);
+       }
+       if(input_buffer)
+       {
+               delete input_buffer;
+       }
+       if(frame_buffer)
+       {
+               delete frame_buffer;
+       }
+       reset_parameters();
+       return 0;
+}
+
+int VDeviceLML::read_buffer(VFrame *frame)
+{
+       long first_field = 0, frame1_size = 0, frame2_size = 0, i;
+       int result = 0, frame_no = 0, retries = 0;
+
+       if(!jvideo_fd) return 1;
+
+       input_error = 0;
+
+retry:
+       frame->set_compressed_size(0);
+       retries++;
+       if(retries > 5) return 1;
+
+// Keep reading until the first field of a frame arrives.
+       while(!input_error && !first_field)
+       {
+// Get the first marker of a frame
+               while(!input_error && next_bytes(2) != SOI)
+               {
+                       get_byte();
+               }
+
+// Store SOI marker
+               frame_size = 0;
+               write_byte(get_byte());
+               write_byte(get_byte());
+
+// Copy the first frame
+               while(!input_error && next_bytes(2) != EOI)
+               {
+// Replace the LML header with a Quicktime header
+                       if(next_bytes(2) == APP3)
+                       {
+                               first_field = 1;
+                               write_fake_marker();
+                       
+                               get_byte(); // APP3
+                               get_byte();
+                               get_byte(); // LEN
+                               get_byte();
+                               get_byte(); // COMMENT
+                               get_byte();
+                               get_byte();
+                               get_byte();
+                               get_byte(); // Frame no
+                               get_byte();
+                               get_byte(); // sec
+                               get_byte();
+                               get_byte();
+                               get_byte();
+                               get_byte(); // usec
+                               get_byte();
+                               get_byte();
+                               get_byte();
+                               get_byte(); // framesize (useless since we have to swap frames)
+                               get_byte();
+                               get_byte();
+                               get_byte();
+                               frame_no = get_byte(); // frame seq no
+                               frame_no |= (long)get_byte() << 8;
+                               frame_no |= (long)get_byte() << 16;
+                               frame_no |= (long)get_byte() << 24;
+
+                               if(frame_no <= last_frame_no)
+                               {
+                                       input_error = reopen_input();
+                                       first_field = 0;
+                                       goto retry;
+                               }
+                               else
+                               {
+// Finish LML header
+                                       last_frame_no = frame_no;
+                                       while(next_bytes(2) != 0xffdb) get_byte();
+                               }
+                       }
+                       else
+                       {
+                               write_byte(get_byte());
+                       }
+               }
+
+// Store EOI marker
+               write_byte(get_byte());
+               write_byte(get_byte());
+       }
+
+       frame1_size = frame_size;
+
+// Read the second field
+       if(first_field)
+       {
+// Find next field
+               while(!input_error && next_bytes(2) != SOI)
+               {
+                       get_byte();
+               }
+
+// Store SOI marker
+               write_byte(get_byte());
+               write_byte(get_byte());
+
+// Store Quicktime header
+               write_fake_marker();
+
+// Copy the second frame
+               while(!input_error && next_bytes(2) != EOI)
+               {
+                       write_byte(get_byte());
+               }
+
+// Store EOI marker
+               write_byte(get_byte());
+               write_byte(get_byte());
+       }
+
+       frame2_size = frame_size - frame1_size;
+
+// Insert the required information
+       if(!input_error)
+       {
+// Store in the VFrame
+               frame->allocate_compressed_data(frame_size);
+
+// Quicktime expects the even field first
+               if(device->odd_field_first)
+               {
+                       memcpy(frame->get_data(), frame_buffer + frame1_size, frame2_size);
+                       memcpy(frame->get_data() + frame2_size, frame_buffer, frame1_size);
+               }
+               else
+                       memcpy(frame->get_data(), frame_buffer, frame_size);
+
+               frame->set_compressed_size(frame_size);
+       }
+       else
+       {
+               input_error = 0;
+               reopen_input();
+               goto retry;
+       }
+
+       return input_error;
+}
+
+int VDeviceLML::reopen_input()
+{
+       int input_error = 0;
+       Timer timer;
+       fprintf(stderr, _("VDeviceLML::read_buffer: driver crash\n"));
+       fclose(jvideo_fd);
+       timer.delay(100);
+       input_error = open_input();
+       if(!input_error) fprintf(stderr, _("VDeviceLML::read_buffer: reopened\n"));
+       last_frame_no = 0;
+       input_position = INPUT_BUFFER_SIZE;
+       return input_error;
+}
+
+
+int VDeviceLML::write_fake_marker()
+{
+// Marker
+       write_byte(0xff);
+       write_byte(0xe1);
+// Size
+       write_byte(0x00);
+       write_byte(0x2a);
+// Blank space
+       for(int i = 0; i < 0x28; i++)
+       {
+               write_byte(0x00);
+       }
+       return 0;
+}
+
+int VDeviceLML::refill_input()
+{
+// Shift remaining data up.
+       memcpy(input_buffer, input_buffer + input_position, INPUT_BUFFER_SIZE - input_position);
+
+// Append new data
+       input_error = !fread(input_buffer + INPUT_BUFFER_SIZE - input_position, 
+                                       INPUT_BUFFER_SIZE - (INPUT_BUFFER_SIZE - input_position), 
+                                       1,
+                                       jvideo_fd);
+
+       input_position = 0;
+       return input_error;
+}
+
+
+int VDeviceLML::write_buffer(VFrame *frame, EDL *edl)
+{
+       int result = 0, i, frame1size, j, size_qword, real_size, skip;
+       unsigned long size = frame->get_compressed_size();
+       unsigned char *data = frame->get_data();
+       unsigned char *data1;
+       int even_field_first = 1;
+
+#if 0
+       if(!jvideo_fd || frame->get_color_model() != VFRAME_COMPRESSED) return 1;
+#endif
+
+       if(frame_allocated < size * 2)
+       {
+               delete frame_buffer;
+               frame_buffer = 0;
+       }
+       
+       if(!frame_buffer)
+       {
+               frame_buffer = new unsigned char[size * 2];
+       }
+
+       for(data1 = data + 1, i = 0; i < size - 1; i++)
+               if(data[i] == ((EOI & 0xff00) >> 8) && data1[i] == (EOI & 0xff)) break;
+
+       i += 2;
+       frame1size = i;
+       j = 0;
+       if(even_field_first) i = 0;
+
+// SOI
+       frame_buffer[j++] = data[i++];
+       frame_buffer[j++] = data[i++];
+
+// APP3 for LML driver
+       frame_buffer[j++] = (APP3 & 0xff00) >> 8;
+       frame_buffer[j++] = APP3 & 0xff;
+       frame_buffer[j++] = 0;       // Marker size
+       frame_buffer[j++] = 0x2c;
+       frame_buffer[j++] = 'L';     // nm
+       frame_buffer[j++] = 'M';
+       frame_buffer[j++] = 'L';
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;       // frameNo
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;       // sec
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;       // usec
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+// Frame size eventually goes here
+       size_qword = j;      
+       frame_buffer[j++] = 0;           
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+// Frame Seq No
+       frame_buffer[j++] = 0;           
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+// Color Encoding
+       frame_buffer[j++] = 1;           
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+// Video Stream
+       frame_buffer[j++] = 1;           
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+// Time Decimation
+       frame_buffer[j++] = 1;           
+       frame_buffer[j++] = 0;
+// Filler
+       frame_buffer[j++] = 0;           
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;           
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+       frame_buffer[j++] = 0;
+
+// Copy rest of first field
+       data1 = data + 1;
+       
+       while(i < size)
+       {
+               frame_buffer[j++] = data[i++];
+       }
+
+// Copy second field
+       if(!even_field_first)
+       {
+               for(i = 0; i < frame1size; )
+               {
+                       frame_buffer[j++] = data[i++];
+               }
+       }
+
+       real_size = j;
+// frameSize in little endian
+       frame_buffer[size_qword++] = (real_size & 0xff);
+       frame_buffer[size_qword++] = ((real_size & 0xff00) >> 8);
+       frame_buffer[size_qword++] = ((real_size & 0xff0000) >> 16);
+       frame_buffer[size_qword++] = ((real_size & 0xff000000) >> 24);
+
+//fwrite(frame_buffer, real_size, 1, stdout);
+       result = !fwrite(frame_buffer, 
+               real_size, 
+               1, 
+               jvideo_fd);
+       if(result) perror("VDeviceLML::write_buffer");
+
+       return result;
+}
+
+ArrayList<int>* VDeviceLML::get_render_strategies()
+{
+       return &render_strategies;
+}