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