You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

399 lines
8.3 KiB

/*
* 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;
}