/* * 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_FIREWIRE #include "audiodevice.h" #include "condition.h" #include "iec61883output.h" #include "mutex.h" #include "playbackconfig.h" #include "bctimer.h" #include "vframe.h" #include "videodevice.h" #include #include #include #include #include #include #include // Crazy DV internals #define CIP_N_NTSC 2436 #define CIP_D_NTSC 38400 #define CIP_N_PAL 1 #define CIP_D_PAL 16 #define OUTPUT_SAMPLES 262144 #define BUFFER_TIMEOUT 500000 IEC61883Output::IEC61883Output(AudioDevice *adevice) : Thread(1, 0, 0) { reset(); this->adevice = adevice; } IEC61883Output::IEC61883Output(VideoDevice *vdevice) : Thread(1, 0, 0) { reset(); this->vdevice = vdevice; } IEC61883Output::~IEC61883Output() { if(Thread::running()) { done = 1; start_lock->unlock(); Thread::cancel(); } Thread::join(); if(buffer) { for(int i = 0; i < total_buffers; i++) { if(buffer[i]) delete [] buffer[i]; } delete [] buffer; delete [] buffer_size; delete [] buffer_valid; } if(audio_lock) delete audio_lock; if(video_lock) delete video_lock; if(start_lock) delete start_lock; if(audio_buffer) delete [] audio_buffer; if(temp_frame) delete temp_frame; if(temp_frame2) delete temp_frame2; if(video_encoder) dv_delete(video_encoder); if(audio_encoder) dv_delete(audio_encoder); if(encoder) dv_delete(encoder); if(buffer_lock) delete buffer_lock; if(position_lock) delete position_lock; if(frame) iec61883_dv_close(frame); if(handle) raw1394_destroy_handle(handle); } void IEC61883Output::reset() { handle = 0; fd = 0; frame = 0; out_position = 0; out_buffer = 0; out_size = 0; buffer = 0; buffer_size = 0; total_buffers = 0; current_inbuffer = 0; current_outbuffer = 0; done = 0; audio_lock = 0; video_lock = 0; start_lock = 0; buffer_lock = 0; position_lock = 0; video_encoder = 0; audio_encoder = 0; encoder = 0; audio_buffer = 0; audio_samples = 0; temp_frame = 0; temp_frame2 = 0; audio_position = 0; interrupted = 0; have_video = 0; adevice = 0; vdevice = 0; is_pal = 0; } static int read_frame_static(unsigned char *data, int n, unsigned int dropped, void *ptr) { IEC61883Output *output = (IEC61883Output*)ptr; return output->read_frame(data, n, dropped); } int IEC61883Output::open(int port, int channel, int length, int channels, int bits, int samplerate, int syt) { this->channels = channels; this->bits = bits; this->samplerate = samplerate; this->total_buffers = length; this->syt = syt; // Set PAL mode based on frame height if(vdevice) is_pal = (vdevice->out_h == 576); if(!handle) { handle = raw1394_new_handle_on_port(port); if(handle) { frame = iec61883_dv_xmit_init(handle, is_pal, read_frame_static, (void *)this); if(frame) { if(!iec61883_dv_xmit_start(frame, channel)) { fd = raw1394_get_fd(handle); } } } // Create buffers buffer = new char*[total_buffers]; bzero(buffer, sizeof(char*) * total_buffers); for(int i = 0; i < length; i++) buffer[i] = new char[DV_PAL_SIZE]; buffer_size = new int[total_buffers]; buffer_valid = new int[total_buffers]; bzero(buffer_size, sizeof(int) * total_buffers); bzero(buffer_valid, sizeof(int) * total_buffers); video_lock = new Condition(0, "IEC61883Output::video_lock"); audio_lock = new Condition(0, "IEC61883Output::audio_lock"); start_lock = new Condition(0, "IEC61883Output::start_lock"); buffer_lock = new Mutex("IEC61883Output::buffer_lock"); position_lock = new Mutex("IEC61883Output::position_lock"); encoder = dv_new(); audio_buffer = new char[OUTPUT_SAMPLES * channels * bits / 8]; Thread::start(); } return 0; } void IEC61883Output::run() { Thread::enable_cancel(); start_lock->lock("IEC61883Output::run"); Thread::disable_cancel(); while(!done) { struct timeval tv; fd_set rfds; FD_ZERO (&rfds); FD_SET (fd, &rfds); tv.tv_sec = 0; tv.tv_usec = 20000; if(select(fd + 1, &rfds, 0, 0, &tv) > 0) raw1394_loop_iterate (handle); } } int IEC61883Output::read_frame(unsigned char *data, int n, unsigned int dropped) { // Next frame if(!out_buffer || out_position + 480 > out_size) { buffer_lock->lock("IEC61883Output read_frame 1"); out_buffer = buffer[current_outbuffer]; out_size = buffer_size[current_outbuffer]; out_position = 0; // No video. Put in a fake frame for audio only if(!have_video) { #include "data/fake_ntsc_dv.h" out_size = sizeof(fake_ntsc_dv) - 4; out_buffer = (char*)fake_ntsc_dv + 4; } // Calculate number of samples needed based on given pattern for // norm. int samples_per_frame = 2048; // Encode audio if(audio_samples > samples_per_frame) { int samples_written = dv_write_audio(encoder, (unsigned char*)out_buffer, (unsigned char*)audio_buffer, samples_per_frame, channels, bits, samplerate, is_pal ? DV_PAL : DV_NTSC); memcpy(audio_buffer, audio_buffer + samples_written * bits * channels / 8, (audio_samples - samples_written) * bits * channels / 8); audio_samples -= samples_written; position_lock->lock("IEC61883Output::run"); audio_position += samples_written; position_lock->unlock(); audio_lock->unlock(); } buffer_lock->unlock(); } // Write next chunk of current frame if(out_buffer && out_position + 480 <= out_size) { memcpy(data, out_buffer + out_position, 480); out_position += 480; if(out_position >= out_size) { buffer_lock->lock("IEC61883Output read_frame 2"); buffer_valid[current_outbuffer] = 0; // Advance buffer number if possible increment_counter(¤t_outbuffer); // Reuse same buffer next time if(!buffer_valid[current_outbuffer]) { decrement_counter(¤t_outbuffer); } else // Wait for user to reach current buffer before unlocking any more. { video_lock->unlock(); } buffer_lock->unlock(); } } return 0; } void IEC61883Output::write_frame(VFrame *input) { VFrame *ptr = 0; int result = 0; //printf("IEC61883Output::write_frame 1\n"); if(fd <= 0) return; if(interrupted) return; // Encode frame to DV if(input->get_color_model() != BC_COMPRESSED) { if(!temp_frame) temp_frame = new VFrame; if(!encoder) encoder = dv_new(); ptr = temp_frame; // Exact resolution match. Don't do colorspace conversion if(input->get_color_model() == BC_YUV422 && input->get_w() == 720 && (input->get_h() == 480 || input->get_h() == 576)) { int norm = is_pal ? DV_PAL : DV_NTSC; int data_size = is_pal ? DV_PAL_SIZE : DV_NTSC_SIZE; temp_frame->allocate_compressed_data(data_size); temp_frame->set_compressed_size(data_size); dv_write_video(encoder, temp_frame->get_data(), input->get_rows(), BC_YUV422, norm); ptr = temp_frame; } else // Convert resolution and color model before compressing { if(!temp_frame2) { int h = input->get_h(); // Default to NTSC if unknown if(h != 480 && h != 576) h = 480; temp_frame2 = new VFrame(720, h, BC_YUV422, 0); } int norm = is_pal ? DV_PAL : DV_NTSC; int data_size = is_pal ? DV_PAL_SIZE : DV_NTSC_SIZE; temp_frame->allocate_compressed_data(data_size); temp_frame->set_compressed_size(data_size); BC_CModels::transfer(temp_frame2->get_rows(), /* Leave NULL if non existent */ input->get_rows(), temp_frame2->get_y(), /* Leave NULL if non existent */ temp_frame2->get_u(), temp_frame2->get_v(), input->get_y(), /* Leave NULL if non existent */ input->get_u(), input->get_v(), 0, /* Dimensions to capture from input frame */ 0, MIN(temp_frame2->get_w(), input->get_w()), MIN(temp_frame2->get_h(), input->get_h()), 0, /* Dimensions to project on output frame */ 0, MIN(temp_frame2->get_w(), input->get_w()), MIN(temp_frame2->get_h(), input->get_h()), input->get_color_model(), BC_YUV422, 0, /* When transfering BC_RGBA8888 to non-alpha this is the background color in 0xRRGGBB hex */ input->get_bytes_per_line(), /* For planar use the luma rowspan */ temp_frame2->get_bytes_per_line()); /* For planar use the luma rowspan */ dv_write_video(encoder, temp_frame->get_data(), temp_frame2->get_rows(), BC_YUV422, norm); ptr = temp_frame; } } else ptr = input; // Take over buffer table buffer_lock->lock("IEC61883Output::write_frame 1"); have_video = 1; // Wait for buffer to become available with timeout while(buffer_valid[current_inbuffer] && !result && !interrupted) { buffer_lock->unlock(); result = video_lock->timed_lock(BUFFER_TIMEOUT); buffer_lock->lock("IEC61883Output::write_frame 2"); } // Write buffer if there's room if(!buffer_valid[current_inbuffer]) { if(!buffer[current_inbuffer]) { buffer[current_inbuffer] = new char[ptr->get_compressed_size()]; buffer_size[current_inbuffer] = ptr->get_compressed_size(); } memcpy(buffer[current_inbuffer], ptr->get_data(), ptr->get_compressed_size()); buffer_valid[current_inbuffer] = 1; increment_counter(¤t_inbuffer); } else // Ignore it if there isn't room. { ; } buffer_lock->unlock(); start_lock->unlock(); //printf("IEC61883Output::write_frame 100\n"); } void IEC61883Output::write_samples(char *data, int samples) { //printf("IEC61883Output::write_samples 1\n"); int result = 0; //int timeout = (samples * 1000000LL * 2) / samplerate; if(interrupted) return; //printf("IEC61883Output::write_samples 2\n"); // Check for maximum sample count exceeded if(samples > OUTPUT_SAMPLES) { printf("IEC61883Output::write_samples samples=%d > OUTPUT_SAMPLES=%d\n", samples, OUTPUT_SAMPLES); return; } //printf("IEC61883Output::write_samples 3\n"); // Take over buffer table buffer_lock->lock("IEC61883Output::write_samples 1"); // Wait for buffer to become available with timeout while(audio_samples > OUTPUT_SAMPLES - samples && !result && !interrupted) { buffer_lock->unlock(); result = audio_lock->timed_lock(BUFFER_TIMEOUT); buffer_lock->lock("IEC61883Output::write_samples 2"); } if(!interrupted && audio_samples <= OUTPUT_SAMPLES - samples) { //printf("IEC61883Output::write_samples 4 %d\n", audio_samples); memcpy(audio_buffer + audio_samples * channels * bits / 8, data, samples * channels * bits / 8); audio_samples += samples; } buffer_lock->unlock(); start_lock->unlock(); //printf("IEC61883Output::write_samples 100\n"); } long IEC61883Output::get_audio_position() { position_lock->lock("IEC61883Output::get_audio_position"); long result = audio_position; position_lock->unlock(); return result; } void IEC61883Output::interrupt() { interrupted = 1; // Break write_samples out of a lock video_lock->unlock(); audio_lock->unlock(); // Playback should stop when the object is deleted. } void IEC61883Output::flush() { } void IEC61883Output::increment_counter(int *counter) { (*counter)++; if(*counter >= total_buffers) *counter = 0; } void IEC61883Output::decrement_counter(int *counter) { (*counter)--; if(*counter < 0) *counter = total_buffers - 1; } #endif // HAVE_FIREWIRE