/* * 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 * */ #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* VDeviceLML::get_render_strategies() { return &render_strategies; }