/* * clone.c * * Copyright (C) Thomas Oestreich - June 2001 * * This file is part of transcode, a video stream processing tool * * transcode 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, or (at your option) * any later version. * * transcode 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 GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ //undef if you experience lock ups! #define USE_FIFO_LOGFILE 1 #include "transcode.h" #include "libtc/libtc.h" #include "encoder.h" #include "clone.h" #include "seqinfo.h" /* for sync_type_t */ #include "ivtc.h" #include "frame_info.h" static FILE *pfd=NULL; static int clone_ctr=0, sync_disabled_flag=0; static int width, height, vcodec; static char *video_buffer=NULL, *pulldown_buffer=NULL; static int sync_ctr=0, frame_ctr=0, drop_ctr=0, seq_dis=-1; static char *logfile; static int sfd=0; static double fps; static pthread_t thread=(pthread_t)0; static int clone_read_thread_flag=0; static pthread_mutex_t buffer_fill_lock=PTHREAD_MUTEX_INITIALIZER; static int buffer_fill_ctr; static pthread_cond_t buffer_fill_cv=PTHREAD_COND_INITIALIZER; int clone_init(FILE *fd) { vob_t *vob; // copy file pointer pfd=fd; vob = tc_get_vob(); fps = vob->fps; width = vob->im_v_width; height = vob->im_v_height; vcodec = vob->im_v_codec; //sync log file if((sfd = open(logfile, O_RDONLY, 0666))<0) { tc_log_perror(__FILE__, "open file"); return(-1); } if(verbose & TC_DEBUG) tc_log_msg(__FILE__, "reading video frame sync data from %s", logfile); // allocate space, assume max buffer size if((video_buffer = tc_zalloc(width*height*3))==NULL) { tc_log_error(__FILE__, "out of memory"); sync_disabled_flag=1; return(-1); } // allocate space, assume max buffer size if((pulldown_buffer = tc_zalloc(width*height*3))==NULL) { tc_log_error(__FILE__, "out of memory"); sync_disabled_flag=1; return(-1); } //basic operational flags clone_read_thread_flag=1; sync_disabled_flag=0; if(pthread_create(&thread, NULL, (void *) clone_read_thread, NULL)!=0) { tc_log_error(__FILE__, "failed to start frame processing thread"); sync_disabled_flag=1; return(-1); } return(0); } static frame_info_list_t *fiptr=NULL; static int buffered_p_read(char *s) { pthread_mutex_lock(&buffer_fill_lock); //exit if(buffer_fill_ctr <= 0 && clone_read_thread_flag==0) { pthread_mutex_unlock(&buffer_fill_lock); return(0); } if(verbose & TC_SYNC) tc_log_msg(__FILE__, "WAIT (%d)", buffer_fill_ctr); while(buffer_fill_ctr == 0) { pthread_cond_wait(&buffer_fill_cv, &buffer_fill_lock); #ifdef BROKEN_PTHREADS // Used to be MacOSX specific; kernel 2.6 as well? pthread_testcancel(); #endif } --buffer_fill_ctr; pthread_mutex_unlock(&buffer_fill_lock); fiptr=frame_info_retrieve(); ac_memcpy(s, fiptr->sync_info, sizeof(sync_info_t)); return(sizeof(sync_info_t)); } static int get_next_frame(char *buffer, int size) { int clone_flag; int i, ret=0; double drift=0; sync_info_t ptr; // read next log file entry: //default clone_flag=1; if(sync_disabled_flag) goto read_only; if(verbose & TC_SYNC) tc_log_msg(__FILE__, "----------------- reading syncinfo (%d)", sync_ctr); if((i=buffered_p_read((char *) &ptr)) != sizeof(sync_info_t)) { if(verbose & TC_DEBUG) { tc_log_msg(__FILE__, "read error (%d/%ld)", i, (long)sizeof(sync_info_t)); } //no more frames? sync_disabled_flag=1; return(-1); } //only relevant information clone_flag = ptr.adj_frame; // infos: if(verbose & TC_COUNTER) { if(ptr.sequence != seq_dis) { drift = ptr.dec_fps - fps; tc_log_msg(__FILE__, "frame=%6ld seq=%4ld adj=%4d AV=%8.4f [fps] ratio= %.4f PTS= %.2f", ptr.enc_frame, ptr.sequence, drop_ctr, drift, ((fps>0)?ptr.enc_fps/fps:0.0f), ptr.pts); if(ptr.drop_seq) { tc_log_msg(__FILE__, "MPEG sequence (%ld) dropped for AV sync correction", ptr.sequence); } seq_dis=ptr.sequence; } } drop_ctr += (clone_flag-1); tc_update_frames_dropped(clone_flag-1); ++sync_ctr; read_only: if(verbose & TC_SYNC) tc_log_msg(__FILE__, "reading frame (%d)", frame_ctr); ret = fread(buffer, size, 1, pfd); if(ret!=1) { sync_disabled_flag=1; return(-1); } ++frame_ctr; // this number determines the number of frame copies, including master // frame // ---------------------------------------------------------------- // // new: reverse 3:2 pulldown (inverse telecine) // currently, only a few number of hardcoded sequences // indicated by pulldown flag, are supported, s. below: if(ptr.pulldown > 0) ivtc(&clone_flag, ptr.pulldown, buffer, pulldown_buffer, width, height, size, vcodec, verbose); //free frame info buffer frame_info_remove(fiptr); fiptr=NULL; return(clone_flag); } //import API int clone_frame(char *buffer, int size) { int i=0; if(clone_ctr) { //copy already buffered frame ac_memcpy(buffer, video_buffer, size); --clone_ctr; return(0); } //we loop until we have a valid frame; for (;;) { i=get_next_frame(buffer, size); //error or eos if(i==-1) return(-1); if(i==1) return(0); //unique frame, already loaded in buffer if(i>1) { //frame will be cloned. We need to get a copy first ac_memcpy(video_buffer, buffer, size); clone_ctr=i-1; return(0); } //frame dropped, get next one } return(0); } void clone_close() { void *status; // cancel the thread if (thread) { pthread_cancel(thread); #ifdef BROKEN_PTHREADS // Used to be MacOSX specific; kernel 2.6 as well? pthread_cond_signal(&buffer_fill_cv); #endif pthread_join(thread, &status); thread = (pthread_t)0; } //reentrance safe if(video_buffer != NULL) free(video_buffer); video_buffer=NULL; if(pulldown_buffer != NULL) free(pulldown_buffer); pulldown_buffer=NULL; if(sfd>0) { close(sfd); unlink(logfile); free(logfile); sfd=0; } if (pfd) pclose(pfd); pfd = NULL; } char *clone_fifo() { char *name, *a, b[PATH_MAX]; //need to create a pipe here if ((a = getenv("TMPDIR")) != NULL) tc_snprintf(b, PATH_MAX, "%s/%s", a, "fileXXXXXX"); else tc_snprintf(b, PATH_MAX, "%s/%s", "/tmp", "fileXXXXXX"); name = mktemp(b); logfile=tc_strdup(name); #ifdef USE_FIFO_LOGFILE if(mkfifo(logfile, 0666)<0) { tc_log_perror(__FILE__, "create FIFO"); return(NULL); } #endif return(logfile); } void clone_read_thread() { frame_info_list_t *ptr = NULL; int i=0, j=0; for(;;) { if((ptr = frame_info_register(i))==NULL) { tc_log_error(__FILE__, "could not allocate a frame info buffer"); pthread_mutex_lock(&buffer_fill_lock); clone_read_thread_flag=0; pthread_mutex_unlock(&buffer_fill_lock); pthread_exit(0); return; } if((ptr->sync_info = tc_zalloc(sizeof(sync_info_t)))==NULL) { tc_log_error(__FILE__, "out of memory"); pthread_mutex_lock(&buffer_fill_lock); clone_read_thread_flag=0; pthread_mutex_unlock(&buffer_fill_lock); pthread_exit(0); } if(verbose & TC_SYNC) tc_log_msg(__FILE__, "READ (%d)", i); if((j=tc_pread(sfd, (uint8_t *) ptr->sync_info, sizeof(sync_info_t))) != sizeof(sync_info_t)) { if(verbose & TC_DEBUG) tc_log_msg(__FILE__, "tc_pread error (%d/%ld)", j, (long)sizeof(sync_info_t)); pthread_mutex_lock(&buffer_fill_lock); clone_read_thread_flag=0; pthread_mutex_unlock(&buffer_fill_lock); pthread_exit(0); } // ready for encoding frame_info_set_status(ptr, FRAME_INFO_READY); pthread_mutex_lock(&buffer_fill_lock); ++buffer_fill_ctr; //tc_log_msg(__FILE__, "fill (%d)", buffer_fill_ctr); //notify import thread pthread_cond_signal(&buffer_fill_cv); pthread_mutex_unlock(&buffer_fill_lock); ++i; } return; }