summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
authorJohn Stebbins <[email protected]>2015-11-03 09:46:28 -0800
committerJohn Stebbins <[email protected]>2015-11-09 11:06:59 -0800
commita986f54a4109bb8c3b2fb57849b4b0ea01e3c522 (patch)
tree7add9d007dc8bdd72114637c8f3f7361621ad4a5 /libhb
parent63b340ebc33d57aeda22e340dab48b1df2106541 (diff)
libhb: make muxer, sync, and reader behave like other work objects
simplify job initialization sequence, clean up code, and document dependencies in the sequence better. Make hb_add set job->sequence_id. It is no longer necessary for the frontend to do this. If the frontend needs the sequence_id, it is returned by hb_add(). Clean up use of interjob. do_job() now uses sequence_id to detect when a new sequence of related jobs is running and automatically clears interjob.
Diffstat (limited to 'libhb')
-rw-r--r--libhb/common.c20
-rw-r--r--libhb/common.h11
-rw-r--r--libhb/decomb.c1
-rw-r--r--libhb/denoise.c3
-rw-r--r--libhb/enc_qsv.c2
-rw-r--r--libhb/encavcodec.c26
-rw-r--r--libhb/enctheora.c17
-rw-r--r--libhb/encx264.c30
-rw-r--r--libhb/encx265.c3
-rw-r--r--libhb/hb.c8
-rw-r--r--libhb/hb.h12
-rw-r--r--libhb/hb_json.c4
-rw-r--r--libhb/internal.h7
-rw-r--r--libhb/muxavformat.c11
-rw-r--r--libhb/muxcommon.c275
-rw-r--r--libhb/reader.c657
-rw-r--r--libhb/scan.c2
-rw-r--r--libhb/stream.c2
-rw-r--r--libhb/sync.c79
-rw-r--r--libhb/work.c1465
20 files changed, 1298 insertions, 1337 deletions
diff --git a/libhb/common.c b/libhb/common.c
index 47f1b26f9..3e91635be 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -694,6 +694,26 @@ const hb_rate_t* hb_video_framerate_get_next(const hb_rate_t *last)
return ((hb_rate_internal_t*)last)->next;
}
+int hb_video_framerate_get_close(hb_rational_t *framerate, double thresh)
+{
+ double fps_in;
+ const hb_rate_t * rate = NULL;
+ int result = -1;
+ double closest = thresh;
+
+ fps_in = (double)framerate->num / framerate->den;
+ while ((rate = hb_video_framerate_get_next(rate)) != NULL)
+ {
+ double fps = (double)hb_video_rate_clock / rate->rate;
+ if (ABS(fps - fps_in) < closest)
+ {
+ result = rate->rate;
+ closest = ABS(fps - fps_in);
+ }
+ }
+ return result;
+}
+
int hb_audio_samplerate_get_best(uint32_t codec, int samplerate, int *sr_shift)
{
int best_samplerate;
diff --git a/libhb/common.h b/libhb/common.h
index a78f86475..92d911320 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -58,6 +58,9 @@
#ifndef MAX
#define MAX( a, b ) ( (a) > (b) ? (a) : (b) )
#endif
+#ifndef ABS
+#define ABS(a) ((a) > 0 ? (a) : (-(a)))
+#endif
#define HB_ALIGN(x, a) (((x)+(a)-1)&~((a)-1))
@@ -365,6 +368,8 @@ const char* hb_video_framerate_get_name(int framerate);
const char* hb_video_framerate_sanitize_name(const char *name);
void hb_video_framerate_get_limits(int *low, int *high, int *clock);
const hb_rate_t* hb_video_framerate_get_next(const hb_rate_t *last);
+int hb_video_framerate_get_close(hb_rational_t *framerate,
+ double thresh);
int hb_audio_samplerate_get_best(uint32_t codec, int samplerate, int *sr_shift);
int hb_audio_samplerate_get_from_name(const char *name);
@@ -531,6 +536,10 @@ struct hb_job_s
double vquality;
int vbitrate;
hb_rational_t vrate;
+ // Some parameters that depend on vrate (like keyint) can't change
+ // between encoding passes. So orig_vrate is used to store the
+ // 1st pass rate.
+ hb_rational_t orig_vrate;
int cfr;
PRIVATE int pass_id;
int twopass; // Enable 2-pass encode. Boolean
@@ -1139,12 +1148,12 @@ struct hb_work_object_s
hb_thread_t * thread;
int yield;
volatile int * done;
+ volatile int * die;
int status;
int codec_param;
hb_title_t * title;
hb_work_object_t * next;
- int thread_sleep_interval;
hb_handle_t * h;
#endif
diff --git a/libhb/decomb.c b/libhb/decomb.c
index 3eaee3251..1da26adbc 100644
--- a/libhb/decomb.c
+++ b/libhb/decomb.c
@@ -73,7 +73,6 @@ which will feed EEDI2 interpolations to yadif.
#define PARITY_DEFAULT -1
-#define ABS(a) ((a) > 0 ? (a) : (-(a)))
#define MIN3(a,b,c) MIN(MIN(a,b),c)
#define MAX3(a,b,c) MAX(MAX(a,b),c)
diff --git a/libhb/denoise.c b/libhb/denoise.c
index 2b6c6c4cf..f66c987be 100644
--- a/libhb/denoise.c
+++ b/libhb/denoise.c
@@ -24,9 +24,6 @@
#define HQDN3D_SPATIAL_CHROMA_DEFAULT 3.0f
#define HQDN3D_TEMPORAL_LUMA_DEFAULT 6.0f
-#define ABS(A) ( (A) > 0 ? (A) : -(A) )
-#define MIN( a, b ) ( (a) > (b) ? (b) : (a) )
-
struct hb_filter_private_s
{
short hqdn3d_coef[6][512*16];
diff --git a/libhb/enc_qsv.c b/libhb/enc_qsv.c
index 4f99bbc95..360db65b5 100644
--- a/libhb/enc_qsv.c
+++ b/libhb/enc_qsv.c
@@ -1023,7 +1023,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
// set the keyframe interval
if (pv->param.gop.gop_pic_size < 0)
{
- int rate = (int)((double)job->vrate.num / (double)job->vrate.den + 0.5);
+ int rate = (double)job->orig_vrate.num / job->orig_vrate.den + 0.5;
if (pv->param.videoParam->mfx.RateControlMethod == MFX_RATECONTROL_CQP)
{
// ensure B-pyramid is enabled for CQP on Haswell
diff --git a/libhb/encavcodec.c b/libhb/encavcodec.c
index 134dbcc6b..9cc4193b5 100644
--- a/libhb/encavcodec.c
+++ b/libhb/encavcodec.c
@@ -102,17 +102,8 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job )
// Set things in context that we will allow the user to
// override with advanced settings.
- if( job->pass_id == HB_PASS_ENCODE_2ND )
- {
- hb_interjob_t * interjob = hb_interjob_get( job->h );
- fps.den = interjob->vrate.den;
- fps.num = interjob->vrate.num;
- }
- else
- {
- fps.den = job->vrate.den;
- fps.num = job->vrate.num;
- }
+ fps.den = job->vrate.den;
+ fps.num = job->vrate.num;
// If the fps.num is the internal clock rate, there's a good chance
// this is a standard rate that we have in our hb_video_rates table.
@@ -163,7 +154,8 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job )
context->time_base.den = fps.num;
context->time_base.num = fps.den;
- context->gop_size = 10 * ((double)job->vrate.num / job->vrate.den + 0.5);
+ context->gop_size = ((double)job->orig_vrate.num / job->orig_vrate.den +
+ 0.5) * 10;
/* place job->encoder_options in an hb_dict_t for convenience */
hb_dict_t * lavc_opts = NULL;
@@ -234,6 +226,16 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job )
context->sample_aspect_ratio.num = job->par.num;
context->sample_aspect_ratio.den = job->par.den;
+ if (job->vcodec == HB_VCODEC_FFMPEG_MPEG4)
+ {
+ // MPEG-4 Part 2 stores the PAR num/den as unsigned 8-bit fields,
+ // and libavcodec's encoder fails to initialize if we don't
+ // reduce it to fit 8-bits.
+ hb_limit_rational(&context->sample_aspect_ratio.num,
+ &context->sample_aspect_ratio.den,
+ context->sample_aspect_ratio.num,
+ context->sample_aspect_ratio.den, 255);
+ }
hb_log( "encavcodec: encoding with stored aspect %d/%d",
job->par.num, job->par.den );
diff --git a/libhb/enctheora.c b/libhb/enctheora.c
index a49d7ff6b..376b442ba 100644
--- a/libhb/enctheora.c
+++ b/libhb/enctheora.c
@@ -71,18 +71,8 @@ int enctheoraInit( hb_work_object_t * w, hb_job_t * job )
ti.frame_width = (job->width + 0xf) & ~0xf;
ti.frame_height = (job->height + 0xf) & ~0xf;
ti.pic_x = ti.pic_y = 0;
-
- if( job->pass_id == HB_PASS_ENCODE_2ND )
- {
- hb_interjob_t * interjob = hb_interjob_get( job->h );
- ti.fps_numerator = interjob->vrate.num;
- ti.fps_denominator = interjob->vrate.den;
- }
- else
- {
- ti.fps_numerator = job->vrate.num;
- ti.fps_denominator = job->vrate.den;
- }
+ ti.fps_numerator = job->vrate.num;
+ ti.fps_denominator = job->vrate.den;
ti.aspect_numerator = job->par.num;
ti.aspect_denominator = job->par.den;
ti.colorspace = TH_CS_UNSPECIFIED;
@@ -98,7 +88,8 @@ int enctheoraInit( hb_work_object_t * w, hb_job_t * job )
ti.quality = job->vquality;
}
- keyframe_frequency = 10 * ((double)job->vrate.num / job->vrate.den + 0.5);
+ keyframe_frequency = ((double)job->orig_vrate.num / job->orig_vrate.den +
+ 0.5) * 10;
hb_log("theora: keyint: %i", keyframe_frequency);
diff --git a/libhb/encx264.c b/libhb/encx264.c
index 552e713ad..f87573609 100644
--- a/libhb/encx264.c
+++ b/libhb/encx264.c
@@ -92,7 +92,7 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
job->encoder_preset, job->encoder_tune) < 0)
{
free( pv );
- pv = NULL;
+ w->private_data = NULL;
return 1;
}
@@ -120,17 +120,8 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
/* Some HandBrake-specific defaults; users can override them
* using the encoder_options string. */
- if( job->pass_id == HB_PASS_ENCODE_2ND && job->cfr != 1 )
- {
- hb_interjob_t * interjob = hb_interjob_get( job->h );
- param.i_fps_num = interjob->vrate.num;
- param.i_fps_den = interjob->vrate.den;
- }
- else
- {
- param.i_fps_num = job->vrate.num;
- param.i_fps_den = job->vrate.den;
- }
+ param.i_fps_num = job->vrate.num;
+ param.i_fps_den = job->vrate.den;
if ( job->cfr == 1 )
{
param.i_timebase_num = 0;
@@ -146,9 +137,9 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
/* Set min:max keyframe intervals to 1:10 of fps;
* adjust +0.5 for when fps has remainder to bump
* { 23.976, 29.976, 59.94 } to { 24, 30, 60 }. */
- param.i_keyint_min = (double)job->vrate.num / job->vrate.den + 0.5;
+ param.i_keyint_min = (double)job->orig_vrate.num / job->orig_vrate.den +
+ 0.5;
param.i_keyint_max = 10 * param.i_keyint_min;
-
param.i_log_level = X264_LOG_INFO;
/* set up the VUI color model & gamma to match what the COLR atom
@@ -301,7 +292,7 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
if (hb_apply_h264_profile(&param, job->encoder_profile, 1))
{
free(pv);
- pv = NULL;
+ w->private_data = NULL;
return 1;
}
}
@@ -311,7 +302,7 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
job->encoder_profile, 1) < 0)
{
free(pv);
- pv = NULL;
+ w->private_data = NULL;
return 1;
}
}
@@ -354,7 +345,7 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
{
hb_error("encx264: x264_encoder_open failed.");
free( pv );
- pv = NULL;
+ w->private_data = NULL;
return 1;
}
@@ -380,6 +371,11 @@ void encx264Close( hb_work_object_t * w )
{
hb_work_private_t * pv = w->private_data;
+ if (pv == NULL)
+ {
+ // Not initialized
+ return;
+ }
if (pv->delayed_chapters != NULL)
{
struct chapter_s *item;
diff --git a/libhb/encx265.c b/libhb/encx265.c
index d9c15637d..eb449173b 100644
--- a/libhb/encx265.c
+++ b/libhb/encx265.c
@@ -139,7 +139,8 @@ int encx265Init(hb_work_object_t *w, hb_job_t *job)
hb_reduce(&vrate.num, &vrate.den, job->vrate.num, job->vrate.den);
param->fpsNum = vrate.num;
param->fpsDenom = vrate.den;
- param->keyframeMin = (double)vrate.num / vrate.den + 0.5;
+ param->keyframeMin = (double)job->orig_vrate.num / job->orig_vrate.den +
+ 0.5;
param->keyframeMax = param->keyframeMin * 10;
/*
diff --git a/libhb/hb.c b/libhb/hb.c
index 0364a5a15..dedeadaf5 100644
--- a/libhb/hb.c
+++ b/libhb/hb.c
@@ -46,6 +46,7 @@ struct hb_handle_s
/* The thread which processes the jobs. Others threads are launched
from this one (see work.c) */
+ int sequence_id;
hb_list_t * jobs;
hb_job_t * current_job;
volatile int work_die;
@@ -1524,11 +1525,14 @@ hb_job_t* hb_job_copy(hb_job_t * job)
return job_copy;
}
-void hb_add( hb_handle_t * h, hb_job_t * job )
+int hb_add( hb_handle_t * h, hb_job_t * job )
{
hb_job_t *job_copy = hb_job_copy(job);
job_copy->h = h;
+ job_copy->sequence_id = ++h->sequence_id;
hb_list_add(h->jobs, job_copy);
+
+ return job_copy->sequence_id;
}
void hb_job_setup_passes(hb_handle_t * h, hb_job_t * job, hb_list_t * list_pass)
@@ -1968,7 +1972,7 @@ void hb_set_state( hb_handle_t * h, hb_state_t * s )
{
// Set which job is being worked on
if (h->current_job)
- h->state.param.working.sequence_id = h->current_job->sequence_id & 0xFFFFFF;
+ h->state.param.working.sequence_id = h->current_job->sequence_id;
else
h->state.param.working.sequence_id = 0;
}
diff --git a/libhb/hb.h b/libhb/hb.h
index c8101ed68..0436703a0 100644
--- a/libhb/hb.h
+++ b/libhb/hb.h
@@ -92,7 +92,7 @@ void hb_add_filter( hb_job_t * job, hb_filter_object_t * filter,
/* Handling jobs */
int hb_count( hb_handle_t * );
hb_job_t * hb_job( hb_handle_t *, int );
-void hb_add( hb_handle_t *, hb_job_t * );
+int hb_add( hb_handle_t *, hb_job_t * );
void hb_rem( hb_handle_t *, hb_job_t * );
hb_title_t * hb_find_title_by_index( hb_handle_t *h, int title_index );
@@ -111,11 +111,11 @@ void hb_system_sleep_prevent(hb_handle_t*);
/* Persistent data between jobs. */
typedef struct hb_interjob_s
{
- int last_job; /* job->sequence_id & 0xFFFFFF */
- int frame_count; /* number of frames counted by sync */
- int out_frame_count; /* number of frames counted by render */
- uint64_t total_time; /* real length in 90kHz ticks (i.e. seconds / 90000) */
- hb_rational_t vrate; /* actual measured output vrate from 1st pass */
+ int sequence_id; /* job->sequence_id */
+ int frame_count; /* number of frames counted by sync */
+ int out_frame_count; /* number of frames counted by render */
+ int64_t total_time; /* measured length in 90kHz ticks */
+ hb_rational_t vrate; /* measured output vrate */
hb_subtitle_t *select_subtitle; /* foreign language scan subtitle */
} hb_interjob_t;
diff --git a/libhb/hb_json.c b/libhb/hb_json.c
index ec2a8cae1..f9a4a3aa1 100644
--- a/libhb/hb_json.c
+++ b/libhb/hb_json.c
@@ -1374,9 +1374,7 @@ int hb_add_json( hb_handle_t * h, const char * json_job )
hb_job_t job;
job.json = json_job;
- hb_add(h, &job);
-
- return 0;
+ return hb_add(h, &job);
}
diff --git a/libhb/internal.h b/libhb/internal.h
index 6401f7b4b..79a01dd9b 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -274,10 +274,13 @@ hb_thread_t * hb_scan_init( hb_handle_t *, volatile int * die,
hb_thread_t * hb_work_init( hb_list_t * jobs,
volatile int * die, hb_error_code * error, hb_job_t ** job );
void ReadLoop( void * _w );
+void hb_work_loop( void * );
hb_work_object_t * hb_muxer_init( hb_job_t * );
hb_work_object_t * hb_get_work( hb_handle_t *, int );
-hb_work_object_t * hb_codec_decoder( hb_handle_t *, int );
-hb_work_object_t * hb_codec_encoder( hb_handle_t *, int );
+hb_work_object_t * hb_audio_decoder( hb_handle_t *, int );
+hb_work_object_t * hb_audio_encoder( hb_handle_t *, int );
+hb_work_object_t * hb_video_decoder( hb_handle_t *, int, int );
+hb_work_object_t * hb_video_encoder( hb_handle_t *, int );
/***********************************************************************
* sync.c
diff --git a/libhb/muxavformat.c b/libhb/muxavformat.c
index bfa377089..0d7059775 100644
--- a/libhb/muxavformat.c
+++ b/libhb/muxavformat.c
@@ -355,16 +355,7 @@ static int avformatInit( hb_mux_object_t * m )
track->st->codec->height = job->height;
track->st->disposition |= AV_DISPOSITION_DEFAULT;
- hb_rational_t vrate;
- if( job->pass_id == HB_PASS_ENCODE_2ND )
- {
- hb_interjob_t * interjob = hb_interjob_get( job->h );
- vrate = interjob->vrate;
- }
- else
- {
- vrate = job->vrate;
- }
+ hb_rational_t vrate = job->vrate;
// If the vrate is the internal clock rate, there's a good chance
// this is a standard rate that we have in our hb_video_rates table.
diff --git a/libhb/muxcommon.c b/libhb/muxcommon.c
index 990614540..452e41fb0 100644
--- a/libhb/muxcommon.c
+++ b/libhb/muxcommon.c
@@ -43,7 +43,6 @@ typedef struct
typedef struct
{
hb_lock_t * mutex;
- int ref;
int done;
hb_mux_object_t * m;
double pts; // end time of next muxing chunk
@@ -60,9 +59,10 @@ typedef struct
struct hb_work_private_s
{
- hb_job_t * job;
- int track;
- hb_mux_t * mux;
+ hb_job_t * job;
+ int track;
+ hb_mux_t * mux;
+ hb_list_t * list_work;
};
@@ -444,6 +444,7 @@ static int muxWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
if ( i >= mux->ntracks )
{
mux->done = 1;
+ *w->done = 1;
hb_unlock( mux->mutex );
hb_bitvec_free(&more);
return HB_WORK_DONE;
@@ -477,147 +478,133 @@ static void muxFlush(hb_mux_t * mux)
}
}
-static void muxClose( hb_work_object_t * w )
+static void muxClose( hb_work_object_t * muxer )
{
- hb_work_private_t * pv = w->private_data;
- hb_mux_t * mux = pv->mux;
- hb_job_t * job = pv->job;
- hb_track_t * track;
- int i;
+ hb_work_private_t * pv = muxer->private_data;
+ if (pv == NULL)
+ {
+ // Not initialized
+ return;
+ }
+
+ hb_mux_t * mux = pv->mux;
+ hb_job_t * job = pv->job;
+ hb_track_t * track;
+ hb_work_object_t * w;
+ int i;
hb_lock( mux->mutex );
- if ( --mux->ref == 0 )
+ muxFlush(mux);
+
+ // Update state before closing muxer. Closing the muxer
+ // may initiate optimization which can take a while and
+ // we want the muxing state to be visible while this is
+ // happening.
+ if( job->pass_id == HB_PASS_ENCODE ||
+ job->pass_id == HB_PASS_ENCODE_2ND )
{
- muxFlush(mux);
-
- // Update state before closing muxer. Closing the muxer
- // may initiate optimization which can take a while and
- // we want the muxing state to be visible while this is
- // happening.
- if( job->pass_id == HB_PASS_ENCODE ||
- job->pass_id == HB_PASS_ENCODE_2ND )
- {
- /* Update the UI */
- hb_state_t state;
- state.state = HB_STATE_MUXING;
- state.param.muxing.progress = 0;
- hb_set_state( job->h, &state );
- }
+ /* Update the UI */
+ hb_state_t state;
+ state.state = HB_STATE_MUXING;
+ state.param.muxing.progress = 0;
+ hb_set_state( job->h, &state );
+ }
- if( mux->m )
- {
- mux->m->end( mux->m );
- free( mux->m );
- }
+ if( mux->m )
+ {
+ mux->m->end( mux->m );
+ free( mux->m );
+ }
+
+ // we're all done muxing -- print final stats and cleanup.
+ if( job->pass_id == HB_PASS_ENCODE ||
+ job->pass_id == HB_PASS_ENCODE_2ND )
+ {
+ hb_stat_t sb;
+ uint64_t bytes_total, frames_total;
- // we're all done muxing -- print final stats and cleanup.
- if( job->pass_id == HB_PASS_ENCODE ||
- job->pass_id == HB_PASS_ENCODE_2ND )
+ if (!hb_stat(job->file, &sb))
{
- hb_stat_t sb;
- uint64_t bytes_total, frames_total;
+ hb_deep_log( 2, "mux: file size, %"PRId64" bytes", (uint64_t) sb.st_size );
- if (!hb_stat(job->file, &sb))
+ bytes_total = 0;
+ frames_total = 0;
+ for( i = 0; i < mux->ntracks; ++i )
{
- hb_deep_log( 2, "mux: file size, %"PRId64" bytes", (uint64_t) sb.st_size );
-
- bytes_total = 0;
- frames_total = 0;
- for( i = 0; i < mux->ntracks; ++i )
- {
- track = mux->track[i];
- hb_log( "mux: track %d, %"PRId64" frames, %"PRId64" bytes, %.2f kbps, fifo %d",
- i, track->frames, track->bytes,
- 90000.0 * track->bytes / mux->pts / 125,
- track->mf.flen );
- if( !i && job->vquality < 0 )
- {
- /* Video */
- hb_deep_log( 2, "mux: video bitrate error, %+"PRId64" bytes",
- (int64_t)(track->bytes - mux->pts * job->vbitrate * 125 / 90000) );
- }
- bytes_total += track->bytes;
- frames_total += track->frames;
- }
-
- if( bytes_total && frames_total )
+ track = mux->track[i];
+ hb_log( "mux: track %d, %"PRId64" frames, %"PRId64" bytes, %.2f kbps, fifo %d",
+ i, track->frames, track->bytes,
+ 90000.0 * track->bytes / mux->pts / 125,
+ track->mf.flen );
+ if( !i && job->vquality < 0 )
{
- hb_deep_log( 2, "mux: overhead, %.2f bytes per frame",
- (float) ( sb.st_size - bytes_total ) /
- frames_total );
+ /* Video */
+ hb_deep_log( 2, "mux: video bitrate error, %+"PRId64" bytes",
+ (int64_t)(track->bytes - mux->pts * job->vbitrate * 125 / 90000) );
}
+ bytes_total += track->bytes;
+ frames_total += track->frames;
}
- }
- for( i = 0; i < mux->ntracks; ++i )
- {
- hb_buffer_t * b;
- track = mux->track[i];
- while ( (b = mf_pull( mux, i )) != NULL )
- {
- hb_buffer_close( &b );
- }
- if( track->mux_data )
+ if( bytes_total && frames_total )
{
- free( track->mux_data );
- free( track->mf.fifo );
+ hb_deep_log( 2, "mux: overhead, %.2f bytes per frame",
+ (float) ( sb.st_size - bytes_total ) /
+ frames_total );
}
- free( track );
}
- free(mux->track);
- hb_unlock( mux->mutex );
- hb_lock_close( &mux->mutex );
- hb_bitvec_free(&mux->eof);
- hb_bitvec_free(&mux->rdy);
- hb_bitvec_free(&mux->allEof);
- hb_bitvec_free(&mux->allRdy);
- free( mux );
- }
- else
- {
- hb_unlock( mux->mutex );
}
- free( pv );
- w->private_data = NULL;
-}
-
-static void mux_loop( void * _w )
-{
- hb_work_object_t * w = _w;
- hb_work_private_t * pv = w->private_data;
- hb_job_t * job = pv->job;
- hb_buffer_t * buf_in;
- while ( !*job->die && w->status != HB_WORK_DONE )
+ for (i = 0; i < mux->ntracks; ++i)
{
- buf_in = hb_fifo_get_wait( w->fifo_in );
- if ( pv->mux->done )
- break;
- if ( buf_in == NULL )
- continue;
- if ( *job->die )
+ hb_buffer_t * b;
+ track = mux->track[i];
+ while ( (b = mf_pull( mux, i )) != NULL )
{
- if( buf_in )
- {
- hb_buffer_close( &buf_in );
- }
- break;
+ hb_buffer_close( &b );
}
-
- w->status = w->work( w, &buf_in, NULL );
- if( buf_in )
+ if( track->mux_data )
+ {
+ free( track->mux_data );
+ free( track->mf.fifo );
+ }
+ free( track );
+ }
+ free(mux->track);
+ hb_unlock( mux->mutex );
+ hb_lock_close( &mux->mutex );
+ hb_bitvec_free(&mux->eof);
+ hb_bitvec_free(&mux->rdy);
+ hb_bitvec_free(&mux->allEof);
+ hb_bitvec_free(&mux->allRdy);
+ free( mux );
+
+ // Close mux work threads
+ while ((w = hb_list_item(pv->list_work, 0)))
+ {
+ hb_list_rem(pv->list_work, w);
+ if (w->thread != NULL)
{
- hb_buffer_close( &buf_in );
+ hb_thread_close( &w->thread );
}
+ free(w->private_data);
+ free(w);
}
+ hb_list_close(&pv->list_work);
+ free( pv );
+ muxer->private_data = NULL;
}
-hb_work_object_t * hb_muxer_init( hb_job_t * job )
+static int muxInit( hb_work_object_t * muxer, hb_job_t * job )
{
- int i;
- hb_mux_t * mux = calloc( sizeof( hb_mux_t ), 1 );
- hb_work_object_t * w;
- hb_work_object_t * muxer;
+ muxer->private_data = calloc( sizeof( hb_work_private_t ), 1 );
+ hb_work_private_t * pv = muxer->private_data;
+
+ hb_mux_t * mux = calloc( sizeof( hb_mux_t ), 1 );
+ int i;
+ hb_work_object_t * w;
+
+ pv->list_work = hb_list_init();
// The bit vectors must be allocated before hb_thread_init for the
// audio and subtitle muxer jobs below.
@@ -649,7 +636,7 @@ hb_work_object_t * hb_muxer_init( hb_job_t * job )
hb_error( "No muxer selected, exiting" );
*job->done_error = HB_ERROR_INIT;
*job->die = 1;
- return NULL;
+ return -1;
}
/* Create file, write headers */
if( mux->m )
@@ -659,60 +646,50 @@ hb_work_object_t * hb_muxer_init( hb_job_t * job )
}
/* Initialize the work objects that will receive fifo data */
-
- muxer = hb_get_work( job->h, WORK_MUX );
- muxer->private_data = calloc( sizeof( hb_work_private_t ), 1 );
- muxer->private_data->job = job;
- muxer->private_data->mux = mux;
- mux->ref++;
- muxer->private_data->track = mux->ntracks;
+ pv->job = job;
+ pv->mux = mux;
+ pv->track = mux->ntracks;
muxer->fifo_in = job->fifo_mpeg4;
add_mux_track( mux, job->mux_data, 1 );
- muxer->done = &muxer->private_data->mux->done;
- for( i = 0; i < hb_list_count( job->list_audio ); i++ )
+ for (i = 0; i < hb_list_count(job->list_audio); i++)
{
hb_audio_t *audio = hb_list_item( job->list_audio, i );
- w = hb_get_work( job->h, WORK_MUX );
- w->private_data = calloc( sizeof( hb_work_private_t ), 1 );
+ w = hb_get_work(job->h, WORK_MUX);
+ w->private_data = calloc(sizeof(hb_work_private_t), 1);
w->private_data->job = job;
w->private_data->mux = mux;
- mux->ref++;
w->private_data->track = mux->ntracks;
w->fifo_in = audio->priv.fifo_out;
- add_mux_track( mux, audio->priv.mux_data, 1 );
- w->done = &job->done;
- hb_list_add( job->list_work, w );
- w->thread = hb_thread_init( w->name, mux_loop, w, HB_NORMAL_PRIORITY );
+ add_mux_track(mux, audio->priv.mux_data, 1);
+ hb_list_add(pv->list_work, w);
}
- for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
+ for (i = 0; i < hb_list_count(job->list_subtitle); i++)
{
hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
if (subtitle->config.dest != PASSTHRUSUB)
continue;
- w = hb_get_work( job->h, WORK_MUX );
- w->private_data = calloc( sizeof( hb_work_private_t ), 1 );
+ w = hb_get_work(job->h, WORK_MUX);
+ w->private_data = calloc(sizeof(hb_work_private_t), 1);
w->private_data->job = job;
w->private_data->mux = mux;
- mux->ref++;
w->private_data->track = mux->ntracks;
w->fifo_in = subtitle->fifo_out;
- add_mux_track( mux, subtitle->mux_data, 0 );
- w->done = &job->done;
- hb_list_add( job->list_work, w );
- w->thread = hb_thread_init( w->name, mux_loop, w, HB_NORMAL_PRIORITY );
+ add_mux_track(mux, subtitle->mux_data, 0);
+ hb_list_add(pv->list_work, w);
}
- return muxer;
-}
-// muxInit does nothing because the muxer has a special initializer
-// that takes care of initializing all muxer work objects
-static int muxInit( hb_work_object_t * w, hb_job_t * job )
-{
+ /* Launch processing threads */
+ for (i = 0; i < hb_list_count(pv->list_work); i++)
+ {
+ w = hb_list_item(pv->list_work, i);
+ w->done = muxer->done;
+ w->thread = hb_thread_init(w->name, hb_work_loop, w, HB_LOW_PRIORITY);
+ }
return 0;
}
diff --git a/libhb/reader.c b/libhb/reader.c
index dda2a2e71..0c6f6d853 100644
--- a/libhb/reader.c
+++ b/libhb/reader.c
@@ -8,18 +8,21 @@
*/
#include "hb.h"
-static int hb_reader_init( hb_work_object_t * w, hb_job_t * job );
-static void hb_reader_close( hb_work_object_t * w );
+static int reader_init( hb_work_object_t * w, hb_job_t * job );
+static void reader_close( hb_work_object_t * w );
+static int reader_work( hb_work_object_t * w, hb_buffer_t ** buf_in,
+ hb_buffer_t ** buf_out);
hb_work_object_t hb_reader =
{
- WORK_READER,
- "Reader",
- hb_reader_init,
- NULL,
- hb_reader_close,
- NULL,
- NULL
+ .id = WORK_READER,
+ .name = "Reader",
+ .init = reader_init,
+ .work = reader_work,
+ .close = reader_close,
+ .info = NULL,
+ .bsinfo = NULL,
+ .flush = NULL
};
typedef struct
@@ -56,6 +59,7 @@ struct hb_work_private_s
int start_found; // found pts_to_start point
int64_t pts_to_start;
+ int chapter_end;
uint64_t st_first;
uint64_t duration;
hb_fifo_t * fifos[100];
@@ -68,7 +72,7 @@ static hb_fifo_t ** GetFifoForId( hb_work_private_t * r, int id );
static void UpdateState( hb_work_private_t * r, int64_t start);
/***********************************************************************
- * hb_reader_init
+ * reader_init
***********************************************************************
*
**********************************************************************/
@@ -78,27 +82,114 @@ static int hb_reader_open( hb_work_private_t * r )
{
if ( !( r->bd = hb_bd_init( r->h, r->title->path ) ) )
return 1;
+ if(!hb_bd_start(r->bd, r->title))
+ {
+ hb_bd_close(&r->bd);
+ return 1;
+ }
+ if (r->job->start_at_preview)
+ {
+ // XXX code from DecodePreviews - should go into its own routine
+ hb_bd_seek(r->bd, (float)r->job->start_at_preview /
+ (r->job->seek_points ? (r->job->seek_points + 1.0)
+ : 11.0));
+ }
+ else if (r->job->pts_to_start)
+ {
+ // Note, bd seeks always put us to an i-frame. no need
+ // to start decoding early using r->pts_to_start
+ hb_bd_seek_pts(r->bd, r->job->pts_to_start);
+ r->duration -= r->job->pts_to_start;
+ r->job->pts_to_start = 0;
+ r->start_found = 1;
+ }
+ else
+ {
+ hb_bd_seek_chapter(r->bd, r->job->chapter_start);
+ }
+ if (r->job->angle > 1)
+ {
+ hb_bd_set_angle(r->bd, r->job->angle - 1);
+ }
}
- else if ( r->title->type == HB_DVD_TYPE )
+ else if (r->title->type == HB_DVD_TYPE)
{
if ( !( r->dvd = hb_dvd_init( r->h, r->title->path ) ) )
return 1;
+ if(!hb_dvd_start( r->dvd, r->title, r->job->chapter_start))
+ {
+ hb_dvd_close(&r->dvd);
+ return 1;
+ }
+ if (r->job->angle)
+ {
+ hb_dvd_set_angle(r->dvd, r->job->angle);
+ }
+
+ if (r->job->start_at_preview)
+ {
+ hb_dvd_seek(r->dvd, (float)r->job->start_at_preview /
+ (r->job->seek_points ? (r->job->seek_points + 1.0)
+ : 11.0));
+ }
}
- else if ( r->title->type == HB_STREAM_TYPE ||
- r->title->type == HB_FF_STREAM_TYPE )
+ else if (r->title->type == HB_STREAM_TYPE ||
+ r->title->type == HB_FF_STREAM_TYPE)
{
if (!(r->stream = hb_stream_open(r->h, r->title->path, r->title, 0)))
return 1;
+ if (r->job->start_at_preview)
+ {
+ hb_stream_seek(r->stream, (float)(r->job->start_at_preview - 1) /
+ (r->job->seek_points ? (r->job->seek_points + 1.0)
+ : 11.0));
+ }
+ else if (r->job->pts_to_start)
+ {
+ if (hb_stream_seek_ts( r->stream, r->job->pts_to_start ) >= 0)
+ {
+ // Seek takes us to the nearest I-frame before the timestamp
+ // that we want. So we will retrieve the start time of the
+ // first packet we get, subtract that from pts_to_start, and
+ // inspect the reset of the frames in sync.
+ r->start_found = 2;
+ r->duration -= r->job->pts_to_start;
+ }
+ // hb_stream_seek_ts does nothing for TS streams and will return
+ // an error.
+ }
+ else
+ {
+ //
+ // Standard stream, seek to the starting chapter, if set,
+ // and track the end chapter so that we end at the right time.
+ hb_chapter_t *chap;
+ int start = r->job->chapter_start;
+ chap = hb_list_item(r->job->list_chapter, r->job->chapter_end - 1);
+
+ r->chapter_end = chap->index;
+ if (start > 1)
+ {
+ chap = hb_list_item(r->job->list_chapter, start - 1);
+ start = chap->index;
+ }
+
+ /*
+ * Seek to the start chapter.
+ */
+ hb_stream_seek_chapter(r->stream, start);
+ }
}
else
{
// Unknown type, should never happen
return 1;
}
+
return 0;
}
-static int hb_reader_init( hb_work_object_t * w, hb_job_t * job )
+static int reader_init( hb_work_object_t * w, hb_job_t * job )
{
hb_work_private_t * r;
@@ -124,6 +215,7 @@ static int hb_reader_init( hb_work_object_t * w, hb_job_t * job )
r->demux.last_scr = AV_NOPTS_VALUE;
+ r->chapter_end = job->chapter_end;
if ( !job->pts_to_start )
r->start_found = 1;
else
@@ -171,7 +263,7 @@ static int hb_reader_init( hb_work_object_t * w, hb_job_t * job )
}
-static void hb_reader_close( hb_work_object_t * w )
+static void reader_close( hb_work_object_t * w )
{
hb_work_private_t * r = w->private_data;
@@ -359,401 +451,282 @@ static void new_scr_offset( hb_work_private_t *r, hb_buffer_t *buf )
r->scr_changes = r->demux.scr_changes;
}
-/***********************************************************************
- * ReaderFunc
- ***********************************************************************
- *
- **********************************************************************/
-void ReadLoop( void * _w )
+static void reader_send_eof( hb_work_private_t * r )
+{
+ int ii;
+
+ // send eof buffers downstream to decoders to signal we're done.
+ push_buf(r, r->job->fifo_mpeg2, hb_buffer_eof_init());
+
+ hb_audio_t *audio;
+ for (ii = 0; (audio = hb_list_item(r->job->list_audio, ii)); ++ii)
+ {
+ if (audio->priv.fifo_in)
+ push_buf(r, audio->priv.fifo_in, hb_buffer_eof_init());
+ }
+
+ hb_subtitle_t *subtitle;
+ for (ii = 0; (subtitle = hb_list_item(r->job->list_subtitle, ii)); ++ii)
+ {
+ if (subtitle->fifo_in && subtitle->source != SRTSUB)
+ push_buf(r, subtitle->fifo_in, hb_buffer_eof_init());
+ }
+ hb_log("reader: done. %d scr changes", r->demux.scr_changes);
+}
+
+static int reader_work( hb_work_object_t * w, hb_buffer_t ** buf_in,
+ hb_buffer_t ** buf_out)
{
- hb_work_object_t * w = _w;
hb_work_private_t * r = w->private_data;
hb_fifo_t ** fifos;
- hb_buffer_t * buf = NULL;
+ hb_buffer_t * buf;
hb_buffer_list_t list;
- int n;
- int chapter = -1;
- int chapter_end = r->job->chapter_end;
- uint8_t done = 0;
+ int ii, chapter = -1;
+
+ hb_buffer_list_clear(&list);
if (r->bd)
+ chapter = hb_bd_chapter( r->bd );
+ else if (r->dvd)
+ chapter = hb_dvd_chapter( r->dvd );
+ else if (r->stream)
+ chapter = hb_stream_chapter( r->stream );
+
+ if( chapter < 0 )
{
- if( !hb_bd_start( r->bd, r->title ) )
- {
- hb_bd_close( &r->bd );
- return;
- }
- if ( r->job->start_at_preview )
- {
- // XXX code from DecodePreviews - should go into its own routine
- hb_bd_seek( r->bd, (float)r->job->start_at_preview /
- ( r->job->seek_points ? ( r->job->seek_points + 1.0 ) : 11.0 ) );
- }
- else if ( r->job->pts_to_start )
- {
- // Note, bd seeks always put us to an i-frame. no need
- // to start decoding early using r->pts_to_start
- hb_bd_seek_pts( r->bd, r->job->pts_to_start );
- r->duration -= r->job->pts_to_start;
- r->job->pts_to_start = 0;
- r->start_found = 1;
- }
- else
- {
- hb_bd_seek_chapter( r->bd, r->job->chapter_start );
- }
- if (r->job->angle > 1)
- {
- hb_bd_set_angle( r->bd, r->job->angle - 1 );
- }
+ hb_log( "reader: end of the title reached" );
+ reader_send_eof(r);
+ return HB_WORK_DONE;
}
- else if (r->dvd)
+ if( chapter > r->chapter_end )
{
- /*
- * XXX this code is a temporary hack that should go away if/when
- * chapter merging goes away in libhb/dvd.c
- * map the start and end chapter numbers to on-media chapter
- * numbers since chapter merging could cause the handbrake numbers
- * to diverge from the media numbers and, if our chapter_end is after
- * a media chapter that got merged, we'll stop ripping too early.
- */
- int start = r->job->chapter_start;
- hb_chapter_t *chap = hb_list_item( r->job->list_chapter, chapter_end - 1 );
-
- chapter_end = chap->index;
- if (start > 1)
- {
- chap = hb_list_item( r->job->list_chapter, start - 1 );
- start = chap->index;
- }
- /* end chapter mapping XXX */
-
- if( !hb_dvd_start( r->dvd, r->title, start ) )
- {
- hb_dvd_close( &r->dvd );
- return;
- }
- if (r->job->angle)
- {
- hb_dvd_set_angle( r->dvd, r->job->angle );
- }
+ hb_log("reader: end of chapter %d (media %d) reached at media chapter %d",
+ r->job->chapter_end, r->chapter_end, chapter);
+ reader_send_eof(r);
+ return HB_WORK_DONE;
+ }
- if ( r->job->start_at_preview )
+ if (r->bd)
+ {
+ if( (buf = hb_bd_read( r->bd )) == NULL )
{
- // XXX code from DecodePreviews - should go into its own routine
- hb_dvd_seek( r->dvd, (float)r->job->start_at_preview /
- ( r->job->seek_points ? ( r->job->seek_points + 1.0 ) : 11.0 ) );
+ reader_send_eof(r);
+ return HB_WORK_DONE;
}
}
- else if ( r->stream && r->job->start_at_preview )
- {
-
- // XXX code from DecodePreviews - should go into its own routine
- hb_stream_seek( r->stream, (float)( r->job->start_at_preview - 1 ) /
- ( r->job->seek_points ? ( r->job->seek_points + 1.0 ) : 11.0 ) );
-
- }
- else if ( r->stream && r->job->pts_to_start )
+ else if (r->dvd)
{
- if ( hb_stream_seek_ts( r->stream, r->job->pts_to_start ) >= 0 )
+ if( (buf = hb_dvd_read( r->dvd )) == NULL )
{
- // Seek takes us to the nearest I-frame before the timestamp
- // that we want. So we will retrieve the start time of the
- // first packet we get, subtract that from pts_to_start, and
- // inspect the reset of the frames in sync.
- r->start_found = 2;
- r->duration -= r->job->pts_to_start;
+ reader_send_eof(r);
+ return HB_WORK_DONE;
}
- // hb_stream_seek_ts does nothing for TS streams and will return
- // an error.
- }
- else if( r->stream )
+ }
+ else if (r->stream)
{
- /*
- * Standard stream, seek to the starting chapter, if set, and track the
- * end chapter so that we end at the right time.
- */
- int start = r->job->chapter_start;
- hb_chapter_t *chap = hb_list_item( r->job->list_chapter, chapter_end - 1 );
-
- chapter_end = chap->index;
- if (start > 1)
+ if ( (buf = hb_stream_read( r->stream )) == NULL )
{
- chap = hb_list_item( r->job->list_chapter, start - 1 );
- start = chap->index;
+ reader_send_eof(r);
+ return HB_WORK_DONE;
}
-
- /*
- * Seek to the start chapter.
- */
- hb_stream_seek_chapter( r->stream, start );
}
- hb_buffer_list_clear(&list);
+ (hb_demux[r->title->demuxer])(buf, &list, &r->demux);
- while(!*r->die && !r->job->done && !done)
+ while ((buf = hb_buffer_list_rem_head(&list)) != NULL)
{
- if (r->bd)
- chapter = hb_bd_chapter( r->bd );
- else if (r->dvd)
- chapter = hb_dvd_chapter( r->dvd );
- else if (r->stream)
- chapter = hb_stream_chapter( r->stream );
+ fifos = GetFifoForId( r, buf->s.id );
- if( chapter < 0 )
+ if (fifos && r->stream && r->start_found == 2 )
{
- hb_log( "reader: end of the title reached" );
- break;
- }
- if( chapter > chapter_end )
- {
- hb_log( "reader: end of chapter %d (media %d) reached at media chapter %d",
- r->job->chapter_end, chapter_end, chapter );
- break;
- }
-
- if (buf == NULL)
- {
- if (r->bd)
+ // We will inspect the timestamps of each frame in sync
+ // to skip from this seek point to the timestamp we
+ // want to start at.
+ if (buf->s.start != AV_NOPTS_VALUE &&
+ buf->s.start < r->job->pts_to_start)
{
- if( (buf = hb_bd_read( r->bd )) == NULL )
- {
- break;
- }
- }
- else if (r->dvd)
- {
- if( (buf = hb_dvd_read( r->dvd )) == NULL )
- {
- break;
- }
+ r->job->pts_to_start -= buf->s.start;
}
- else if (r->stream)
+ else if ( buf->s.start >= r->job->pts_to_start )
{
- if ( (buf = hb_stream_read( r->stream )) == NULL )
- {
- break;
- }
+ r->job->pts_to_start = 0;
}
+ r->start_found = 1;
}
- (hb_demux[r->title->demuxer])(buf, &list, &r->demux);
-
- while ((buf = hb_buffer_list_rem_head(&list)) != NULL)
+ if ( fifos && ! r->saw_video && !r->job->indepth_scan )
{
- fifos = GetFifoForId( r, buf->s.id );
-
- if (fifos && r->stream && r->start_found == 2 )
+ // The first data packet with a PTS from an audio or video stream
+ // that we're decoding defines 'time zero'. Discard packets until
+ // we get one.
+ if (buf->s.start != AV_NOPTS_VALUE &&
+ buf->s.renderOffset != AV_NOPTS_VALUE &&
+ (buf->s.id == r->title->video_id ||
+ is_audio( r, buf->s.id)))
{
- // We will inspect the timestamps of each frame in sync
- // to skip from this seek point to the timestamp we
- // want to start at.
- if (buf->s.start != AV_NOPTS_VALUE &&
- buf->s.start < r->job->pts_to_start)
- {
- r->job->pts_to_start -= buf->s.start;
- }
- else if ( buf->s.start >= r->job->pts_to_start )
- {
- r->job->pts_to_start = 0;
- }
- r->start_found = 1;
+ // force a new scr offset computation
+ r->scr_changes = r->demux.scr_changes - 1;
+ // create a stream state if we don't have one so the
+ // offset will get computed correctly.
+ id_to_st( r, buf, 1 );
+ r->saw_video = 1;
+ hb_log( "reader: first SCR %"PRId64" id 0x%x DTS %"PRId64,
+ r->demux.last_scr, buf->s.id, buf->s.renderOffset );
}
-
- if ( fifos && ! r->saw_video && !r->job->indepth_scan )
+ else
{
- // The first data packet with a PTS from an audio or video stream
- // that we're decoding defines 'time zero'. Discard packets until
- // we get one.
- if (buf->s.start != AV_NOPTS_VALUE &&
- buf->s.renderOffset != AV_NOPTS_VALUE &&
- (buf->s.id == r->title->video_id ||
- is_audio( r, buf->s.id)))
- {
- // force a new scr offset computation
- r->scr_changes = r->demux.scr_changes - 1;
- // create a stream state if we don't have one so the
- // offset will get computed correctly.
- id_to_st( r, buf, 1 );
- r->saw_video = 1;
- hb_log( "reader: first SCR %"PRId64" id 0x%x DTS %"PRId64,
- r->demux.last_scr, buf->s.id, buf->s.renderOffset );
- }
- else
- {
- fifos = NULL;
- }
+ fifos = NULL;
}
+ }
- if ( r->job->indepth_scan || fifos )
+ if ( r->job->indepth_scan || fifos )
+ {
+ if ( buf->s.renderOffset != AV_NOPTS_VALUE )
{
- if ( buf->s.renderOffset != AV_NOPTS_VALUE )
+ if ( r->scr_changes != r->demux.scr_changes )
{
- if ( r->scr_changes != r->demux.scr_changes )
+ // This is the first audio or video packet after an SCR
+ // change. Compute a new scr offset that would make this
+ // packet follow the last of this stream with the
+ // correct average spacing.
+ stream_timing_t *st = id_to_st( r, buf, 0 );
+
+ // if this is the video stream and we don't have
+ // audio yet or this is an audio stream
+ // generate a new scr
+ if ( st->is_audio ||
+ ( st == r->stream_timing && !r->saw_audio ) )
{
- // This is the first audio or video packet after an SCR
- // change. Compute a new scr offset that would make this
- // packet follow the last of this stream with the
- // correct average spacing.
- stream_timing_t *st = id_to_st( r, buf, 0 );
-
- // if this is the video stream and we don't have
- // audio yet or this is an audio stream
- // generate a new scr
- if ( st->is_audio ||
- ( st == r->stream_timing && !r->saw_audio ) )
+ new_scr_offset( r, buf );
+ r->sub_scr_set = 0;
+ }
+ else
+ {
+ // defer the scr change until we get some
+ // audio since audio has a timestamp per
+ // frame but video & subtitles don't. Clear
+ // the timestamps so the decoder will generate
+ // them from the frame durations.
+ if (is_subtitle(r, buf->s.id) &&
+ buf->s.start != AV_NOPTS_VALUE)
{
- new_scr_offset( r, buf );
- r->sub_scr_set = 0;
+ if (!r->sub_scr_set)
+ {
+ // We can't generate timestamps in the
+ // subtitle decoder as we can for
+ // audio & video. So we need to make
+ // the closest guess that we can
+ // for the subtitles start time here.
+ int64_t last = r->stream_timing[0].last;
+ r->scr_offset = buf->s.start - last;
+ r->sub_scr_set = 1;
+ }
}
else
{
- // defer the scr change until we get some
- // audio since audio has a timestamp per
- // frame but video & subtitles don't. Clear
- // the timestamps so the decoder will generate
- // them from the frame durations.
- if (is_subtitle(r, buf->s.id) &&
- buf->s.start != AV_NOPTS_VALUE)
- {
- if (!r->sub_scr_set)
- {
- // We can't generate timestamps in the
- // subtitle decoder as we can for
- // audio & video. So we need to make
- // the closest guess that we can
- // for the subtitles start time here.
- int64_t last = r->stream_timing[0].last;
- r->scr_offset = buf->s.start - last;
- r->sub_scr_set = 1;
- }
- }
- else
- {
- buf->s.start = AV_NOPTS_VALUE;
- buf->s.renderOffset = AV_NOPTS_VALUE;
- }
+ buf->s.start = AV_NOPTS_VALUE;
+ buf->s.renderOffset = AV_NOPTS_VALUE;
}
}
}
- if ( buf->s.start != AV_NOPTS_VALUE )
- {
- int64_t start = buf->s.start - r->scr_offset;
+ }
+ if ( buf->s.start != AV_NOPTS_VALUE )
+ {
+ int64_t start = buf->s.start - r->scr_offset;
- if (!r->start_found || r->job->indepth_scan)
- {
- UpdateState( r, start );
- }
+ if (!r->start_found || r->job->indepth_scan)
+ {
+ UpdateState( r, start );
+ }
- if (r->job->indepth_scan && r->job->pts_to_stop &&
- start >= r->pts_to_start + r->job->pts_to_stop)
- {
- // sync normally would terminate p-to-p
- // but sync doesn't run during indepth scan
- hb_log( "reader: reached pts %"PRId64", exiting early", start );
- done = 1;
- break;
- }
+ if (r->job->indepth_scan && r->job->pts_to_stop &&
+ start >= r->pts_to_start + r->job->pts_to_stop)
+ {
+ // sync normally would terminate p-to-p
+ // but sync doesn't run during indepth scan
+ hb_log("reader: reached pts %"PRId64", exiting early", start);
+ reader_send_eof(r);
+ hb_buffer_list_close(&list);
+ return HB_WORK_DONE;
+ }
- if (!r->start_found && start >= r->pts_to_start)
- {
- // pts_to_start point found
- r->start_found = 1;
- if (r->stream)
- {
- // libav multi-threaded decoders can get into
- // a bad state if the initial data is not
- // decodable. So try to improve the chances of
- // a good start by waiting for an initial iframe
- hb_stream_set_need_keyframe(r->stream, 1);
- hb_buffer_close( &buf );
- continue;
- }
- }
- // This log is handy when you need to debug timing problems
- //hb_log("id %x scr_offset %"PRId64
- // " start %"PRId64" --> %"PRId64"",
- // buf->s.id, r->scr_offset, buf->s.start,
- // buf->s.start - r->scr_offset);
- buf->s.start -= r->scr_offset;
- if ( buf->s.stop != AV_NOPTS_VALUE )
+ if (!r->start_found && start >= r->pts_to_start)
+ {
+ // pts_to_start point found
+ r->start_found = 1;
+ if (r->stream)
{
- buf->s.stop -= r->scr_offset;
+ // libav multi-threaded decoders can get into
+ // a bad state if the initial data is not
+ // decodable. So try to improve the chances of
+ // a good start by waiting for an initial iframe
+ hb_stream_set_need_keyframe(r->stream, 1);
+ hb_buffer_close( &buf );
+ continue;
}
}
- if ( buf->s.renderOffset != AV_NOPTS_VALUE )
+ // This log is handy when you need to debug timing problems
+ //hb_log("id %x scr_offset %"PRId64
+ // " start %"PRId64" --> %"PRId64"",
+ // buf->s.id, r->scr_offset, buf->s.start,
+ // buf->s.start - r->scr_offset);
+ buf->s.start -= r->scr_offset;
+ if ( buf->s.stop != AV_NOPTS_VALUE )
{
- // This packet is referenced to the same SCR as the last.
- // Adjust timestamp to remove the System Clock Reference
- // offset then update the average inter-packet time
- // for this stream.
- buf->s.renderOffset -= r->scr_offset;
- update_ipt( r, buf );
+ buf->s.stop -= r->scr_offset;
}
-#if 0
- // JAS: This was added to fix a rare "audio time went backward"
- // sync error I found in one sample. But it has a bad side
- // effect on DVDs, causing frequent "adding silence" sync
- // errors. So I am disabling it.
- else
- {
- update_ipt( r, buf );
- }
-#endif
}
- if( fifos )
+ if ( buf->s.renderOffset != AV_NOPTS_VALUE )
{
- if ( !r->start_found )
- {
- hb_buffer_close( &buf );
- continue;
- }
-
- buf->sequence = r->sequence++;
- /* if there are mutiple output fifos, send a copy of the
- * buffer down all but the first (we have to not ship the
- * original buffer or we'll race with the thread that's
- * consuming the buffer & inject garbage into the data stream). */
- for( n = 1; fifos[n] != NULL; n++)
- {
- hb_buffer_t *buf_copy = hb_buffer_init( buf->size );
- buf_copy->s = buf->s;
- memcpy( buf_copy->data, buf->data, buf->size );
- push_buf( r, fifos[n], buf_copy );
- }
- push_buf( r, fifos[0], buf );
- buf = NULL;
+ // This packet is referenced to the same SCR as the last.
+ // Adjust timestamp to remove the System Clock Reference
+ // offset then update the average inter-packet time
+ // for this stream.
+ buf->s.renderOffset -= r->scr_offset;
+ update_ipt( r, buf );
}
+#if 0
+ // JAS: This was added to fix a rare "audio time went backward"
+ // sync error I found in one sample. But it has a bad side
+ // effect on DVDs, causing frequent "adding silence" sync
+ // errors. So I am disabling it.
else
{
- hb_buffer_close( &buf );
+ update_ipt( r, buf );
}
+#endif
}
- }
-
- // send empty buffers downstream to video & audio decoders to signal we're done.
- if( !*r->die && !r->job->done )
- {
- push_buf(r, r->job->fifo_mpeg2, hb_buffer_eof_init());
-
- hb_audio_t *audio;
- for( n = 0; (audio = hb_list_item( r->job->list_audio, n)); ++n )
+ if( fifos )
{
- if ( audio->priv.fifo_in )
- push_buf(r, audio->priv.fifo_in, hb_buffer_eof_init());
- }
+ if ( !r->start_found )
+ {
+ hb_buffer_close( &buf );
+ continue;
+ }
- hb_subtitle_t *subtitle;
- for( n = 0; (subtitle = hb_list_item( r->job->list_subtitle, n)); ++n )
+ buf->sequence = r->sequence++;
+ /* if there are mutiple output fifos, send a copy of the
+ * buffer down all but the first (we have to not ship the
+ * original buffer or we'll race with the thread that's
+ * consuming the buffer & inject garbage into the data stream). */
+ for (ii = 1; fifos[ii] != NULL; ii++)
+ {
+ hb_buffer_t *buf_copy = hb_buffer_init(buf->size);
+ buf_copy->s = buf->s;
+ memcpy(buf_copy->data, buf->data, buf->size);
+ push_buf(r, fifos[ii], buf_copy);
+ }
+ push_buf(r, fifos[0], buf);
+ buf = NULL;
+ }
+ else
{
- if ( subtitle->fifo_in && subtitle->source == VOBSUB)
- push_buf(r, subtitle->fifo_in, hb_buffer_eof_init());
+ hb_buffer_close(&buf);
}
}
hb_buffer_list_close(&list);
-
- hb_log( "reader: done. %d scr changes", r->demux.scr_changes );
+ return HB_WORK_OK;
}
static void UpdateState( hb_work_private_t * r, int64_t start)
@@ -859,7 +832,7 @@ static hb_fifo_t ** GetFifoForId( hb_work_private_t * r, int id )
{
return r->fifos;
}
-
+
if( !job->indepth_scan )
{
for( i = n = 0; i < hb_list_count( job->list_audio ); i++ )
diff --git a/libhb/scan.c b/libhb/scan.c
index 8626ccca8..34805a30a 100644
--- a/libhb/scan.c
+++ b/libhb/scan.c
@@ -1119,7 +1119,7 @@ static void LookForAudio(hb_scan_t *scan, hb_title_t * title, hb_buffer_t * b)
}
hb_fifo_push( audio->priv.scan_cache, b );
- hb_work_object_t *w = hb_codec_decoder(scan->h, audio->config.in.codec);
+ hb_work_object_t *w = hb_audio_decoder(scan->h, audio->config.in.codec);
if ( w == NULL || w->bsinfo == NULL )
{
diff --git a/libhb/stream.c b/libhb/stream.c
index 08c1d47ea..3ddba0256 100644
--- a/libhb/stream.c
+++ b/libhb/stream.c
@@ -3930,7 +3930,7 @@ static void hb_ps_stream_find_streams(hb_stream_t *stream)
static int probe_dts_profile( hb_stream_t *stream, hb_pes_stream_t *pes )
{
hb_work_info_t info;
- hb_work_object_t *w = hb_codec_decoder( stream->h, pes->codec );
+ hb_work_object_t *w = hb_audio_decoder( stream->h, pes->codec );
w->codec_param = pes->codec_param;
int ret = w->bsinfo( w, pes->probe_buf, &info );
diff --git a/libhb/sync.c b/libhb/sync.c
index d3cb4cbde..512c00a8e 100644
--- a/libhb/sync.c
+++ b/libhb/sync.c
@@ -17,8 +17,6 @@
#endif
#define INT64_MIN (-9223372036854775807LL-1)
-#define ABS(a) ((a) < 0 ? -(a) : (a))
-
typedef struct
{
/* Audio/Video sync thread synchronization */
@@ -27,8 +25,6 @@ typedef struct
int64_t volatile * last_pts;
int pts_count;
- int ref; /* Reference count to tell us when it's unused */
-
/* PTS synchronization */
int64_t audio_pts_slip;
int64_t video_pts_slip;
@@ -37,6 +33,9 @@ typedef struct
/* point-to-point support */
int start_found;
int count_frames;
+
+ /* sync audio work objects */
+ hb_list_t * list_work;
} hb_sync_common_t;
typedef struct
@@ -117,7 +116,7 @@ static hb_buffer_t * OutputAudioFrame( hb_audio_t *audio, hb_buffer_t *buf,
***********************************************************************
* Initialize the work object
**********************************************************************/
-hb_work_object_t * hb_sync_init( hb_job_t * job )
+int syncVideoInit( hb_work_object_t * w, hb_job_t * job)
{
hb_title_t * title = job->title;
hb_chapter_t * chapter;
@@ -125,13 +124,11 @@ hb_work_object_t * hb_sync_init( hb_job_t * job )
uint64_t duration;
hb_work_private_t * pv;
hb_sync_video_t * sync;
- hb_work_object_t * w;
- hb_work_object_t * ret = NULL;
pv = calloc( 1, sizeof( hb_work_private_t ) );
sync = &pv->type.video;
pv->common = calloc( 1, sizeof( hb_sync_common_t ) );
- pv->common->ref++;
+ pv->common->list_work = hb_list_init();
pv->common->mutex = hb_lock_init();
pv->common->next_frame = hb_cond_init();
pv->common->pts_count = 1;
@@ -144,7 +141,6 @@ hb_work_object_t * hb_sync_init( hb_job_t * job )
pv->common->start_found = 1;
}
- ret = w = hb_get_work( job->h, WORK_SYNC_VIDEO );
w->private_data = pv;
w->fifo_in = job->fifo_raw;
// Register condition with fifo to wake us up immediately if
@@ -214,7 +210,17 @@ hb_work_object_t * hb_sync_init( hb_job_t * job )
{
InitSubtitle(job, sync, i);
}
- return ret;
+
+ /* Launch audio processing threads */
+ for (i = 0; i < hb_list_count(pv->common->list_work); i++)
+ {
+ hb_work_object_t * audio_work;
+ audio_work = hb_list_item(pv->common->list_work, i);
+ audio_work->done = w->done;
+ audio_work->thread = hb_thread_init(audio_work->name, hb_work_loop,
+ audio_work, HB_LOW_PRIORITY);
+ }
+ return 0;
}
static void InitSubtitle( hb_job_t * job, hb_sync_video_t * sync, int ii )
@@ -278,7 +284,6 @@ void syncVideoClose( hb_work_object_t * w )
/* Preserve frame count for better accuracy in pass 2 */
hb_interjob_t * interjob = hb_interjob_get( job->h );
interjob->frame_count = pv->common->count_frames;
- interjob->last_job = job->sequence_id;
}
if (sync->drops || sync->dups )
@@ -294,20 +299,24 @@ void syncVideoClose( hb_work_object_t * w )
}
free(sync->subtitle_sanitizer);
- hb_lock( pv->common->mutex );
- if ( --pv->common->ref == 0 )
- {
- hb_unlock( pv->common->mutex );
- hb_cond_close( &pv->common->next_frame );
- hb_lock_close( &pv->common->mutex );
- free((void*)pv->common->last_pts);
- free(pv->common);
- }
- else
+ // Close audio work threads
+ hb_work_object_t * audio_work;
+ while ((audio_work = hb_list_item(pv->common->list_work, 0)))
{
- hb_unlock( pv->common->mutex );
+ hb_list_rem(pv->common->list_work, audio_work);
+ if (audio_work->thread != NULL)
+ {
+ hb_thread_close(&audio_work->thread);
+ }
+ audio_work->close(audio_work);
+ free(audio_work);
}
+ hb_list_close(&pv->common->list_work);
+ hb_cond_close( &pv->common->next_frame );
+ hb_lock_close( &pv->common->mutex );
+ free((void*)pv->common->last_pts);
+ free(pv->common);
free( pv );
w->private_data = NULL;
}
@@ -900,13 +909,6 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
return HB_WORK_OK;
}
-// sync*Init does nothing because sync has a special initializer
-// that takes care of initializing video and all audio tracks
-int syncVideoInit( hb_work_object_t * w, hb_job_t * job)
-{
- return 0;
-}
-
hb_work_object_t hb_sync_video =
{
WORK_SYNC_VIDEO,
@@ -938,20 +940,6 @@ void syncAudioClose( hb_work_object_t * w )
src_delete( sync->state );
}
- hb_lock( pv->common->mutex );
- if ( --pv->common->ref == 0 )
- {
- hb_unlock( pv->common->mutex );
- hb_cond_close( &pv->common->next_frame );
- hb_lock_close( &pv->common->mutex );
- free((void*)pv->common->last_pts);
- free(pv->common);
- }
- else
- {
- hb_unlock( pv->common->mutex );
- }
-
free( pv );
w->private_data = NULL;
}
@@ -1138,10 +1126,11 @@ static void InitAudio( hb_job_t * job, hb_sync_common_t * common, int i )
sync->index = i;
pv->job = job;
pv->common = common;
- pv->common->ref++;
pv->common->pts_count++;
w = hb_get_work( job->h, WORK_SYNC_AUDIO );
+ hb_list_add(common->list_work, w);
+
w->private_data = pv;
w->audio = hb_list_item( job->list_audio, i );
w->fifo_in = w->audio->priv.fifo_raw;
@@ -1356,8 +1345,6 @@ static void InitAudio( hb_job_t * job, hb_sync_common_t * common, int i )
}
sync->gain_factor = pow(10, w->audio->config.out.gain / 20);
-
- hb_list_add( job->list_work, w );
}
static hb_buffer_t * OutputAudioFrame( hb_audio_t *audio, hb_buffer_t *buf,
diff --git a/libhb/work.c b/libhb/work.c
index 52bb900d4..186178d7c 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -28,7 +28,6 @@ typedef struct
static void work_func();
static void do_job( hb_job_t *);
-static void work_loop( void * );
static void filter_loop( void * );
#define FIFO_UNBOUNDED 65536
@@ -72,7 +71,7 @@ static void InitWorkState(hb_handle_t *h, int pass_id, int pass, int pass_count)
p.rate_avg = 0.0;
p.hours = -1;
p.minutes = -1;
- p.seconds = -1;
+ p.seconds = -1;
#undef p
hb_set_state( h, &state );
@@ -117,6 +116,7 @@ static void work_func( void * _work )
break;
}
new_job->h = job->h;
+ new_job->sequence_id = job->sequence_id;
hb_job_close(&job);
job = new_job;
}
@@ -163,7 +163,7 @@ hb_work_object_t * hb_get_work( hb_handle_t *h, int id )
return NULL;
}
-hb_work_object_t* hb_codec_decoder(hb_handle_t *h, int codec)
+hb_work_object_t* hb_audio_decoder(hb_handle_t *h, int codec)
{
hb_work_object_t * w = NULL;
if (codec & HB_ACODEC_FF_MASK)
@@ -183,7 +183,62 @@ hb_work_object_t* hb_codec_decoder(hb_handle_t *h, int codec)
return w;
}
-hb_work_object_t* hb_codec_encoder(hb_handle_t *h, int codec)
+hb_work_object_t* hb_video_decoder(hb_handle_t *h, int vcodec, int param)
+{
+ hb_work_object_t * w;
+
+ w = hb_get_work(h, vcodec);
+ if (w == NULL)
+ {
+ hb_error("Invalid video decoder: codec %d, param %d", vcodec, param);
+ return NULL;
+ }
+ w->codec_param = param;
+
+ return w;
+}
+
+hb_work_object_t* hb_video_encoder(hb_handle_t *h, int vcodec)
+{
+ hb_work_object_t * w = NULL;
+
+ switch (vcodec)
+ {
+ case HB_VCODEC_FFMPEG_MPEG4:
+ w = hb_get_work(h, WORK_ENCAVCODEC);
+ w->codec_param = AV_CODEC_ID_MPEG4;
+ break;
+ case HB_VCODEC_FFMPEG_MPEG2:
+ w = hb_get_work(h, WORK_ENCAVCODEC);
+ w->codec_param = AV_CODEC_ID_MPEG2VIDEO;
+ break;
+ case HB_VCODEC_FFMPEG_VP8:
+ w = hb_get_work(h, WORK_ENCAVCODEC);
+ w->codec_param = AV_CODEC_ID_VP8;
+ break;
+ case HB_VCODEC_X264:
+ w = hb_get_work(h, WORK_ENCX264);
+ break;
+ case HB_VCODEC_QSV_H264:
+ case HB_VCODEC_QSV_H265:
+ w = hb_get_work(h, WORK_ENCQSV);
+ break;
+ case HB_VCODEC_THEORA:
+ w = hb_get_work(h, WORK_ENCTHEORA);
+ break;
+#ifdef USE_X265
+ case HB_VCODEC_X265:
+ w = hb_get_work(h, WORK_ENCX265);
+ break;
+#endif
+ default:
+ hb_error("Unknown video codec (0x%x)", vcodec );
+ }
+
+ return w;
+}
+
+hb_work_object_t* hb_audio_encoder(hb_handle_t *h, int codec)
{
if (codec & HB_ACODEC_FF_MASK)
{
@@ -211,10 +266,10 @@ void hb_display_job_info(hb_job_t *job)
hb_title_t *title = job->title;
hb_audio_t *audio;
hb_subtitle_t *subtitle;
-
+
hb_log("job configuration:");
hb_log( " * source");
-
+
hb_log( " + %s", title->path );
if( job->pts_to_start || job->pts_to_stop )
@@ -267,7 +322,7 @@ void hb_display_job_info(hb_job_t *job)
{
hb_log( " + data rate: %d kbps", title->data_rate / 1000 );
}
-
+
hb_log( " * destination");
hb_log( " + %s", job->file );
@@ -290,9 +345,9 @@ void hb_display_job_info(hb_job_t *job)
{
hb_log( " + chapter markers" );
}
-
+
hb_log(" * video track");
-
+
#ifdef USE_QSV
if (hb_qsv_decode_is_enabled(job))
{
@@ -316,7 +371,7 @@ void hb_display_job_info(hb_job_t *job)
{
hb_log( " + bitrate %d kbps", title->video_bitrate / 1000 );
}
-
+
// Filters can modify dimensions. So show them first.
if( hb_list_count( job->list_filter ) )
{
@@ -339,7 +394,7 @@ void hb_display_job_info(hb_job_t *job)
}
}
}
-
+
hb_log( " + Output geometry" );
hb_log( " + storage dimensions: %d x %d", job->width, job->height );
hb_log( " + pixel aspect ratio: %d : %d", job->par.num, job->par.den );
@@ -441,7 +496,7 @@ void hb_display_job_info(hb_job_t *job)
}
}
- if( job->indepth_scan )
+ if (job->indepth_scan)
{
hb_log( " * Foreign Audio Search: %s%s%s",
job->select_subtitle_config.dest == RENDERSUB ? "Render/Burn-in" : "Passthrough",
@@ -491,7 +546,7 @@ void hb_display_job_info(hb_job_t *job)
audio = hb_list_item( job->list_audio, i );
hb_log( " * audio track %d", audio->config.out.track );
-
+
if( audio->config.out.name )
hb_log( " + name: %s", audio->config.out.name );
@@ -562,85 +617,144 @@ void hb_display_job_info(hb_job_t *job)
}
/* Corrects framerates when actual duration and frame count numbers are known. */
-void correct_framerate( hb_job_t * job )
+void correct_framerate( hb_interjob_t * interjob, hb_job_t * job )
{
- hb_interjob_t * interjob = hb_interjob_get( job->h );
-
- if( ( job->sequence_id & 0xFFFFFF ) != ( interjob->last_job & 0xFFFFFF) )
- return; // Interjob information is for a different encode.
+ if (interjob->total_time <= 0 || interjob->out_frame_count <= 0 ||
+ job->cfr == 1)
+ {
+ // Invalid or uninitialized frame statistics
+ // Or CFR output
+ return;
+ }
// compute actual output vrate from first pass
- interjob->vrate.den = (int64_t)job->vrate.num * interjob->total_time /
- ((int64_t)interjob->out_frame_count * 90000);
- interjob->vrate.num = job->vrate.num;
+ int64_t num, den;
+ num = interjob->out_frame_count * 90000L;
+ den = interjob->total_time;
+ hb_limit_rational64(&num, &den, num, den, INT_MAX);
+
+ job->vrate.num = num;
+ job->vrate.den = den;
+
+ den = hb_video_framerate_get_close(&job->vrate, 2.);
+ if (den > 0)
+ {
+ int low, high, clock;
+ hb_video_framerate_get_limits(&low, &high, &clock);
+ job->vrate.num = clock;
+ job->vrate.den = den;
+ }
+ if (ABS(((double)job->orig_vrate.num / job->orig_vrate.den) -
+ ((double) job->vrate.num / job->vrate.den)) > 0.05)
+ {
+ hb_log("work: correcting framerate, %d/%d -> %d/%d",
+ job->orig_vrate.num, job->orig_vrate.den,
+ job->vrate.num, job->vrate.den);
+ }
}
-/**
- * Job initialization rountine.
- * Initializes fifos.
- * Creates work objects for synchronizer, video decoder, video renderer, video decoder, audio decoder, audio encoder, reader, muxer.
- * Launches thread for each work object with work_loop.
- * Loops while monitoring status of work threads and fifos.
- * Exits loop when conversion is done and fifos are empty.
- * Closes threads and frees fifos.
- * @param job Handle work hb_job_t.
- */
-static void do_job(hb_job_t *job)
+static void analyze_subtitle_scan( hb_job_t * job )
{
+ hb_subtitle_t *subtitle;
+ int subtitle_highest = 0;
+ int subtitle_lowest = 0;
+ int subtitle_lowest_id = 0;
+ int subtitle_forced_id = 0;
+ int subtitle_forced_hits = 0;
+ int subtitle_hit = 0;
int i;
- hb_title_t *title;
- hb_interjob_t *interjob;
- hb_work_object_t *w;
- hb_work_object_t *sync;
- hb_work_object_t *muxer;
- hb_work_object_t *reader = hb_get_work(job->h, WORK_READER);
- hb_audio_t *audio;
- hb_subtitle_t *subtitle;
- unsigned int subtitle_highest = 0;
- unsigned int subtitle_lowest = 0;
- unsigned int subtitle_lowest_id = 0;
- unsigned int subtitle_forced_id = 0;
- unsigned int subtitle_forced_hits = 0;
- unsigned int subtitle_hit = 0;
+ // Before closing the title print out our subtitle stats if we need to
+ // find the highest and lowest.
+ for (i = 0; i < hb_list_count(job->list_subtitle); i++)
+ {
+ subtitle = hb_list_item(job->list_subtitle, i);
- title = job->title;
- interjob = hb_interjob_get( job->h );
+ hb_log("Subtitle track %d (id 0x%x) '%s': %d hits (%d forced)",
+ subtitle->track, subtitle->id, subtitle->lang,
+ subtitle->hits, subtitle->forced_hits);
- if( job->pass_id == HB_PASS_ENCODE_2ND )
- {
- correct_framerate( job );
- }
+ if (subtitle->hits == 0)
+ continue;
- job->list_work = hb_list_init();
+ if (subtitle_highest < subtitle->hits)
+ {
+ subtitle_highest = subtitle->hits;
+ }
- /*
- * OpenCL
- *
- * Note: we delay hb_ocl_init until here, since they're no point it loading
- * the library if we aren't going to use it. But we only call hb_ocl_close
- * in hb_global_close, since un/reloading the library each run is wasteful.
- */
- if (job->use_opencl)
- {
- if (hb_ocl_init() || hb_init_opencl_run_env(0, NULL, "-I."))
+ if (subtitle_lowest == 0 ||
+ subtitle_lowest > subtitle->hits)
{
- hb_log("work: failed to initialize OpenCL environment, using fallback");
- hb_release_opencl_run_env();
- job->use_opencl = 0;
+ subtitle_lowest = subtitle->hits;
+ subtitle_lowest_id = subtitle->id;
}
+
+ // pick the track with fewest forced hits
+ if (subtitle->forced_hits > 0 &&
+ (subtitle_forced_hits == 0 ||
+ subtitle_forced_hits > subtitle->forced_hits))
+ {
+ subtitle_forced_id = subtitle->id;
+ subtitle_forced_hits = subtitle->forced_hits;
+ }
+ }
+
+ if (subtitle_forced_id && job->select_subtitle_config.force)
+ {
+ // If there is a subtitle stream with forced subtitles and forced-only
+ // is set, then select it in preference to the lowest.
+ subtitle_hit = subtitle_forced_id;
+ hb_log("Found a subtitle candidate with id 0x%x (contains forced subs)",
+ subtitle_hit );
+ }
+ else if (subtitle_lowest > 0 && subtitle_lowest < subtitle_highest * 0.1)
+ {
+ // OK we have more than one, and the lowest is lower,
+ // but how much lower to qualify for turning it on by
+ // default?
+ //
+ // Let's say 10% as a default.
+ subtitle_hit = subtitle_lowest_id;
+ hb_log( "Found a subtitle candidate with id 0x%x", subtitle_hit );
}
else
{
- // we're not (re-)using OpenCL here, we can release the environment
- hb_release_opencl_run_env();
+ hb_log( "No candidate detected during subtitle scan" );
}
- hb_log( "starting job" );
+ for (i = 0; i < hb_list_count( job->list_subtitle ); i++)
+ {
+ subtitle = hb_list_item( job->list_subtitle, i );
+ if (subtitle->id == subtitle_hit)
+ {
+ hb_interjob_t *interjob = hb_interjob_get(job->h);
+
+ subtitle->config = job->select_subtitle_config;
+ // Remove from list since we are taking ownership
+ // of the subtitle.
+ hb_list_rem(job->list_subtitle, subtitle);
+ interjob->select_subtitle = subtitle;
+ break;
+ }
+ }
+}
+
+static int sanitize_subtitles( hb_job_t * job )
+{
+ int i;
+ uint8_t one_burned = 0;
+ hb_interjob_t * interjob = hb_interjob_get(job->h);
+ hb_subtitle_t * subtitle;
+
+ if (job->indepth_scan)
+ {
+ // Subtitles are set by hb_add() during subtitle scan
+ return 0;
+ }
/* Look for the scanned subtitle in the existing subtitle list
* select_subtitle implies that we did a scan. */
- if( !job->indepth_scan && interjob->select_subtitle )
+ if (interjob->select_subtitle != NULL)
{
/* Disable forced subtitles if we didn't find any in the scan, so that
* we display normal subtitles instead. */
@@ -649,38 +763,31 @@ static void do_job(hb_job_t *job)
{
interjob->select_subtitle->config.force = 0;
}
- for( i = 0; i < hb_list_count( job->list_subtitle ); )
- {
- subtitle = hb_list_item( job->list_subtitle, i );
- if( subtitle )
+ for (i = 0; i < hb_list_count( job->list_subtitle );)
+ {
+ subtitle = hb_list_item(job->list_subtitle, i);
+ /* Remove the scanned subtitle from the list if
+ * it would result in:
+ * - an emty track (forced and no forced hits)
+ * - an identical, duplicate subtitle track:
+ * -> both (or neither) are forced
+ * -> subtitle is not forced but all its hits are forced */
+ if( ( interjob->select_subtitle->id == subtitle->id ) &&
+ ( ( subtitle->config.force &&
+ interjob->select_subtitle->forced_hits == 0 ) ||
+ ( subtitle->config.force == interjob->select_subtitle->config.force ) ||
+ ( !subtitle->config.force &&
+ interjob->select_subtitle->hits == interjob->select_subtitle->forced_hits ) ) )
{
- /* Remove the scanned subtitle from the list if
- * it would result in:
- * - an emty track (forced and no forced hits)
- * - an identical, duplicate subtitle track:
- * -> both (or neither) are forced
- * -> subtitle is not forced but all its hits are forced */
- if( ( interjob->select_subtitle->id == subtitle->id ) &&
- ( ( subtitle->config.force &&
- interjob->select_subtitle->forced_hits == 0 ) ||
- ( subtitle->config.force == interjob->select_subtitle->config.force ) ||
- ( !subtitle->config.force &&
- interjob->select_subtitle->hits == interjob->select_subtitle->forced_hits ) ) )
- {
- hb_list_rem( job->list_subtitle, subtitle );
- free( subtitle );
- continue;
- }
- /* Adjust output track number, in case we removed one.
- * Output tracks sadly still need to be in sequential order.
- * Note: out.track starts at 1, i starts at 0, and track 1 is interjob->select_subtitle */
- subtitle->out_track = ++i + 1;
- }
- else
- {
- // avoid infinite loop is subtitle == NULL
- i++;
+ hb_list_rem( job->list_subtitle, subtitle );
+ free( subtitle );
+ continue;
}
+ /* Adjust output track number, in case we removed one.
+ * Output tracks sadly still need to be in sequential order.
+ * Note: out.track starts at 1, i starts at 0, and track 1 is
+ * interjob->select_subtitle */
+ subtitle->out_track = ++i + 1;
}
/* Add the subtitle that we found on the subtitle scan pass.
@@ -703,77 +810,307 @@ static void do_job(hb_job_t *job)
}
}
- if ( !job->indepth_scan )
+ for (i = 0; i < hb_list_count(job->list_subtitle);)
{
- // Sanitize subtitles
- uint8_t one_burned = 0;
- for( i = 0; i < hb_list_count( job->list_subtitle ); )
+ subtitle = hb_list_item(job->list_subtitle, i);
+ if (subtitle->config.dest == RENDERSUB)
{
- subtitle = hb_list_item( job->list_subtitle, i );
- if ( subtitle->config.dest == RENDERSUB )
+ if (one_burned)
{
- if ( one_burned )
+ if (!hb_subtitle_can_pass(subtitle->source, job->mux))
{
- if ( !hb_subtitle_can_pass(subtitle->source, job->mux) )
- {
- hb_log( "More than one subtitle burn-in requested, dropping track %d.", i );
- hb_list_rem( job->list_subtitle, subtitle );
- free( subtitle );
- continue;
- }
- else
- {
- hb_log( "More than one subtitle burn-in requested. Changing track %d to soft subtitle.", i );
- subtitle->config.dest = PASSTHRUSUB;
- }
+ hb_log( "More than one subtitle burn-in requested, dropping track %d.", i );
+ hb_list_rem(job->list_subtitle, subtitle);
+ free(subtitle);
+ continue;
}
- else if ( !hb_subtitle_can_burn(subtitle->source) )
+ else
{
- hb_log( "Subtitle burn-in requested and input track can not be rendered. Changing track %d to soft subtitle.", i );
+ hb_log("More than one subtitle burn-in requested. Changing track %d to soft subtitle.", i);
subtitle->config.dest = PASSTHRUSUB;
}
+ }
+ else if (!hb_subtitle_can_burn(subtitle->source))
+ {
+ hb_log("Subtitle burn-in requested and input track can not be rendered. Changing track %d to soft subtitle.", i);
+ subtitle->config.dest = PASSTHRUSUB;
+ }
+ else
+ {
+ one_burned = 1;
+ }
+ }
+
+ if (subtitle->config.dest == PASSTHRUSUB &&
+ !hb_subtitle_can_pass(subtitle->source, job->mux))
+ {
+ if (!one_burned)
+ {
+ hb_log("Subtitle pass-thru requested and input track is not compatible with container. Changing track %d to burned-in subtitle.", i);
+ subtitle->config.dest = RENDERSUB;
+ subtitle->config.default_track = 0;
+ one_burned = 1;
+ }
+ else
+ {
+ hb_log("Subtitle pass-thru requested and input track is not compatible with container. One track already burned, dropping track %d.", i);
+ hb_list_rem(job->list_subtitle, subtitle);
+ free(subtitle);
+ continue;
+ }
+ }
+ /* Adjust output track number, in case we removed one.
+ * Output tracks sadly still need to be in sequential order.
+ * Note: out.track starts at 1, i starts at 0 */
+ subtitle->out_track = ++i;
+ }
+ if (one_burned)
+ {
+ // Add subtitle rendering filter
+ // Note that if the filter is already in the filter chain, this
+ // has no effect. Note also that this means the front-end is
+ // not required to add the subtitle rendering filter since
+ // we will always try to do it here.
+ hb_filter_object_t *filter = hb_filter_init(HB_FILTER_RENDER_SUB);
+ hb_add_filter(job, filter, NULL);
+ }
+
+ return 0;
+}
+
+static int sanitize_audio(hb_job_t *job)
+{
+ int i;
+ hb_audio_t * audio;
+
+ if (job->indepth_scan)
+ {
+ // Audio is not processed during subtitle scan
+ return 0;
+ }
+
+ // apply Auto Passthru settings
+ hb_autopassthru_apply_settings(job);
+
+ for (i = 0; i < hb_list_count(job->list_audio);)
+ {
+ audio = hb_list_item(job->list_audio, i);
+ if (audio->config.out.codec == HB_ACODEC_AUTO_PASS)
+ {
+ // Auto Passthru should have been handled above
+ // remove track to avoid a crash
+ hb_log("Auto Passthru error, dropping track %d",
+ audio->config.out.track);
+ hb_list_rem(job->list_audio, audio);
+ free(audio);
+ continue;
+ }
+ if ((audio->config.out.codec & HB_ACODEC_PASS_FLAG) &&
+ !(audio->config.in.codec &
+ audio->config.out.codec & HB_ACODEC_PASS_MASK))
+ {
+ hb_log("Passthru requested and input codec is not the same as output codec for track %d, dropping track",
+ audio->config.out.track);
+ hb_list_rem(job->list_audio, audio);
+ free(audio);
+ continue;
+ }
+ /* Adjust output track number, in case we removed one.
+ * Output tracks sadly still need to be in sequential order.
+ * Note: out.track starts at 1, i starts at 0 */
+ audio->config.out.track = ++i;
+ }
+
+ int best_mixdown = 0;
+ int best_bitrate = 0;
+ int best_samplerate = 0;
+
+ for (i = 0; i < hb_list_count(job->list_audio); i++)
+ {
+ audio = hb_list_item(job->list_audio, i);
+
+ /* Passthru audio */
+ if (audio->config.out.codec & HB_ACODEC_PASS_FLAG)
+ {
+ // Muxer needs these to be set correctly in order to
+ // set audio track MP4 time base.
+ audio->config.out.samples_per_frame =
+ audio->config.in.samples_per_frame;
+ audio->config.out.samplerate = audio->config.in.samplerate;
+ continue;
+ }
+
+ /* Vorbis language information */
+ if (audio->config.out.codec == HB_ACODEC_VORBIS)
+ audio->priv.config.vorbis.language = audio->config.lang.simple;
+
+ /* sense-check the requested samplerate */
+ if (audio->config.out.samplerate <= 0)
+ {
+ // if not specified, set to same as input
+ audio->config.out.samplerate = audio->config.in.samplerate;
+ }
+ best_samplerate =
+ hb_audio_samplerate_get_best(audio->config.out.codec,
+ audio->config.out.samplerate,
+ NULL);
+ if (best_samplerate != audio->config.out.samplerate)
+ {
+ hb_log("work: sanitizing track %d unsupported samplerate %d Hz to %s kHz",
+ audio->config.out.track, audio->config.out.samplerate,
+ hb_audio_samplerate_get_name(best_samplerate));
+ audio->config.out.samplerate = best_samplerate;
+ }
+
+ /* sense-check the requested mixdown */
+ if (audio->config.out.mixdown <= HB_AMIXDOWN_NONE)
+ {
+ /* Mixdown not specified, set the default mixdown */
+ audio->config.out.mixdown =
+ hb_mixdown_get_default(audio->config.out.codec,
+ audio->config.in.channel_layout);
+ hb_log("work: mixdown not specified, track %d setting mixdown %s",
+ audio->config.out.track,
+ hb_mixdown_get_name(audio->config.out.mixdown));
+ }
+ else
+ {
+ best_mixdown =
+ hb_mixdown_get_best(audio->config.out.codec,
+ audio->config.in.channel_layout,
+ audio->config.out.mixdown);
+ if (audio->config.out.mixdown != best_mixdown)
+ {
+ /* log the output mixdown */
+ hb_log("work: sanitizing track %d mixdown %s to %s",
+ audio->config.out.track,
+ hb_mixdown_get_name(audio->config.out.mixdown),
+ hb_mixdown_get_name(best_mixdown));
+ audio->config.out.mixdown = best_mixdown;
+ }
+ }
+
+ /* sense-check the requested compression level */
+ if (audio->config.out.compression_level < 0)
+ {
+ audio->config.out.compression_level =
+ hb_audio_compression_get_default(audio->config.out.codec);
+ if (audio->config.out.compression_level >= 0)
+ {
+ hb_log("work: compression level not specified, track %d setting compression level %.2f",
+ audio->config.out.track,
+ audio->config.out.compression_level);
+ }
+ }
+ else
+ {
+ float best_compression =
+ hb_audio_compression_get_best(audio->config.out.codec,
+ audio->config.out.compression_level);
+ if (best_compression != audio->config.out.compression_level)
+ {
+ if (best_compression == -1)
+ {
+ hb_log("work: track %d, compression level not supported by codec",
+ audio->config.out.track);
+ }
else
{
- one_burned = 1;
+ hb_log("work: sanitizing track %d compression level %.2f to %.2f",
+ audio->config.out.track,
+ audio->config.out.compression_level,
+ best_compression);
}
+ audio->config.out.compression_level = best_compression;
}
+ }
- if ( subtitle->config.dest == PASSTHRUSUB &&
- !hb_subtitle_can_pass(subtitle->source, job->mux) )
+ /* sense-check the requested quality */
+ if (audio->config.out.quality != HB_INVALID_AUDIO_QUALITY)
+ {
+ float best_quality =
+ hb_audio_quality_get_best(audio->config.out.codec,
+ audio->config.out.quality);
+ if (best_quality != audio->config.out.quality)
{
- if ( !one_burned )
+ if (best_quality == HB_INVALID_AUDIO_QUALITY)
{
- hb_log( "Subtitle pass-thru requested and input track is not compatible with container. Changing track %d to burned-in subtitle.", i );
- subtitle->config.dest = RENDERSUB;
- subtitle->config.default_track = 0;
- one_burned = 1;
+ hb_log("work: track %d, quality mode not supported by codec",
+ audio->config.out.track);
}
else
{
- hb_log( "Subtitle pass-thru requested and input track is not compatible with container. One track already burned, dropping track %d.", i );
- hb_list_rem( job->list_subtitle, subtitle );
- free( subtitle );
- continue;
+ hb_log("work: sanitizing track %d quality %.2f to %.2f",
+ audio->config.out.track,
+ audio->config.out.quality, best_quality);
}
+ audio->config.out.quality = best_quality;
}
- /* Adjust output track number, in case we removed one.
- * Output tracks sadly still need to be in sequential order.
- * Note: out.track starts at 1, i starts at 0 */
- subtitle->out_track = ++i;
}
- if (one_burned)
+
+ /* sense-check the requested bitrate */
+ if (audio->config.out.quality == HB_INVALID_AUDIO_QUALITY)
{
- // Add subtitle rendering filter
- // Note that if the filter is already in the filter chain, this
- // has no effect. Note also that this means the front-end is
- // not required to add the subtitle rendering filter since
- // we will always try to do it here.
- hb_filter_object_t *filter = hb_filter_init(HB_FILTER_RENDER_SUB);
- hb_add_filter(job, filter, NULL);
+ if (audio->config.out.bitrate <= 0)
+ {
+ /* Bitrate not specified, set the default bitrate */
+ audio->config.out.bitrate =
+ hb_audio_bitrate_get_default(audio->config.out.codec,
+ audio->config.out.samplerate,
+ audio->config.out.mixdown);
+ if (audio->config.out.bitrate > 0)
+ {
+ hb_log("work: bitrate not specified, track %d setting bitrate %d Kbps",
+ audio->config.out.track,
+ audio->config.out.bitrate);
+ }
+ }
+ else
+ {
+ best_bitrate =
+ hb_audio_bitrate_get_best(audio->config.out.codec,
+ audio->config.out.bitrate,
+ audio->config.out.samplerate,
+ audio->config.out.mixdown);
+ if (best_bitrate > 0 &&
+ best_bitrate != audio->config.out.bitrate)
+ {
+ /* log the output bitrate */
+ hb_log("work: sanitizing track %d bitrate %d to %d Kbps",
+ audio->config.out.track,
+ audio->config.out.bitrate, best_bitrate);
+ }
+ audio->config.out.bitrate = best_bitrate;
+ }
+ }
+
+ /* sense-check the requested dither */
+ if (hb_audio_dither_is_supported(audio->config.out.codec))
+ {
+ if (audio->config.out.dither_method ==
+ hb_audio_dither_get_default())
+ {
+ /* "auto", enable with default settings */
+ audio->config.out.dither_method =
+ hb_audio_dither_get_default_method();
+ }
+ }
+ else if (audio->config.out.dither_method !=
+ hb_audio_dither_get_default())
+ {
+ /* specific dither requested but dithering not supported */
+ hb_log("work: track %d, dithering not supported by codec",
+ audio->config.out.track);
}
}
+ return 0;
+}
+static int sanitize_qsv( hb_job_t * job )
+{
#ifdef USE_QSV
+ int i;
+
/*
* XXX: mfxCoreInterface's CopyFrame doesn't work in old drivers, and our
* workaround is really slow. If we have validated CPU-based filters in
@@ -848,7 +1185,7 @@ static void do_job(hb_job_t *job)
{
// cropping and scaling always done via VPP filter
case HB_FILTER_CROP_SCALE:
- if (filter->settings == NULL || *filter->settings == '\0')
+ if (filter->settings == NULL || *filter->settings == 0)
{
// VPP defaults were set above, so not a problem
// however, this should never happen, print an error
@@ -936,6 +1273,93 @@ static void do_job(hb_job_t *job)
}
#endif
+ return 0;
+}
+
+/**
+ * Job initialization rountine.
+ *
+ * Initializes fifos.
+ * Creates work objects for synchronizer, video decoder, video renderer,
+ * video decoder, audio decoder, audio encoder, reader, muxer.
+ * Launches thread for each work object with work_loop.
+ * Waits for completion of last work object.
+ * Closes threads and frees fifos.
+ * @param job Handle work hb_job_t.
+ */
+static void do_job(hb_job_t *job)
+{
+ int i, result;
+ hb_title_t * title;
+ hb_interjob_t * interjob;
+ hb_work_object_t * w;
+ hb_audio_t * audio;
+ hb_subtitle_t * subtitle;
+
+ title = job->title;
+
+ interjob = hb_interjob_get(job->h);
+ job->orig_vrate = job->vrate;
+ if (job->sequence_id != interjob->sequence_id)
+ {
+ // New job sequence, clear interjob
+ hb_subtitle_close(&interjob->select_subtitle);
+ memset(interjob, 0, sizeof(*interjob));
+ interjob->sequence_id = job->sequence_id;
+ }
+ else if (job->pass_id == HB_PASS_ENCODE_2ND)
+ {
+ correct_framerate(interjob, job);
+ }
+
+ job->list_work = hb_list_init();
+ w = hb_get_work(job->h, WORK_READER);
+ hb_list_add(job->list_work, w);
+
+ /*
+ * OpenCL
+ *
+ * Note: we delay hb_ocl_init until here, since they're no point it loading
+ * the library if we aren't going to use it. But we only call hb_ocl_close
+ * in hb_global_close, since un/reloading the library each run is wasteful.
+ */
+ if (job->use_opencl)
+ {
+ if (hb_ocl_init() || hb_init_opencl_run_env(0, NULL, "-I."))
+ {
+ hb_log("work: failed to initialize OpenCL environment, using fallback");
+ hb_release_opencl_run_env();
+ job->use_opencl = 0;
+ }
+ }
+ else
+ {
+ // we're not (re-)using OpenCL here, we can release the environment
+ hb_release_opencl_run_env();
+ }
+
+ hb_log( "starting job" );
+
+ // This must be performed before initializing filters because
+ // it can add the subtitle render filter.
+ result = sanitize_subtitles(job);
+ if (result)
+ {
+ *job->done_error = HB_ERROR_WRONG_INPUT;
+ *job->die = 1;
+ goto cleanup;
+ }
+
+ // sanitize_qsv looks for subtitle render filter, so must happen after
+ // sanitize_subtitle
+ result = sanitize_qsv(job);
+ if (result)
+ {
+ *job->done_error = HB_ERROR_WRONG_INPUT;
+ *job->die = 1;
+ goto cleanup;
+ }
+
#ifdef USE_HWD
/*
* Check support for and enable DXVA2-accelerated when applicable; we need:
@@ -955,7 +1379,7 @@ static void do_job(hb_job_t *job)
// Filters have an effect on settings.
// So initialize the filters and update the job.
- if( job->list_filter && hb_list_count( job->list_filter ) )
+ if (job->list_filter && hb_list_count(job->list_filter))
{
hb_filter_init_t init;
@@ -966,13 +1390,14 @@ static void do_job(hb_job_t *job)
init.geometry.par = job->par;
memcpy(init.crop, title->crop, sizeof(int[4]));
- init.vrate = title->vrate;
+ init.vrate = job->vrate;
init.cfr = 0;
init.grayscale = 0;
for( i = 0; i < hb_list_count( job->list_filter ); )
{
hb_filter_object_t * filter = hb_list_item( job->list_filter, i );
- if( filter->init( filter, &init ) )
+ filter->done = &job->done;
+ if (filter->init(filter, &init))
{
hb_log( "Failure to initialise filter '%s', disabling",
filter->name );
@@ -1018,16 +1443,6 @@ static void do_job(hb_job_t *job)
}
/*
- * MPEG-4 Part 2 stores the PAR num/den as unsigned 8-bit fields,
- * and libavcodec's encoder fails to initialize if we don't handle it.
- */
- if (job->vcodec == HB_VCODEC_FFMPEG_MPEG4)
- {
- hb_limit_rational(&job->par.num, &job->par.den,
- job->par.num, job->par.den, 255);
- }
-
- /*
* The frame rate may affect the bitstream's time base, lose superfluous
* factors for consistency (some encoders reduce fractions, some don't).
*/
@@ -1053,285 +1468,133 @@ static void do_job(hb_job_t *job)
job->fifo_render = NULL; // Attached to filter chain
}
- /* Audio fifos must be initialized before sync */
- if (!job->indepth_scan)
+ result = sanitize_audio(job);
+ if (result)
{
- // apply Auto Passthru settings
- hb_autopassthru_apply_settings(job);
- // sanitize audio settings
- for (i = 0; i < hb_list_count(job->list_audio);)
- {
- audio = hb_list_item(job->list_audio, i);
- if (audio->config.out.codec == HB_ACODEC_AUTO_PASS)
- {
- // Auto Passthru should have been handled above
- // remove track to avoid a crash
- hb_log("Auto Passthru error, dropping track %d",
- audio->config.out.track);
- hb_list_rem(job->list_audio, audio);
- free(audio);
- continue;
- }
- if ((audio->config.out.codec & HB_ACODEC_PASS_FLAG) &&
- !(audio->config.in.codec &
- audio->config.out.codec & HB_ACODEC_PASS_MASK))
- {
- hb_log("Passthru requested and input codec is not the same as output codec for track %d, dropping track",
- audio->config.out.track);
- hb_list_rem(job->list_audio, audio);
- free(audio);
- continue;
- }
- /* Adjust output track number, in case we removed one.
- * Output tracks sadly still need to be in sequential order.
- * Note: out.track starts at 1, i starts at 0 */
- audio->config.out.track = ++i;
- }
-
- int best_mixdown = 0;
- int best_bitrate = 0;
- int best_samplerate = 0;
+ *job->done_error = HB_ERROR_WRONG_INPUT;
+ *job->die = 1;
+ goto cleanup;
+ }
+ if (!job->indepth_scan)
+ {
+ // Set up audio decoder work objects
+ // Audio fifos must be initialized before sync
for (i = 0; i < hb_list_count(job->list_audio); i++)
{
audio = hb_list_item(job->list_audio, i);
- /* set up the audio work structures */
+ /* set up the audio work fifos */
audio->priv.fifo_raw = hb_fifo_init(FIFO_SMALL, FIFO_SMALL_WAKE);
audio->priv.fifo_sync = hb_fifo_init(FIFO_SMALL, FIFO_SMALL_WAKE);
audio->priv.fifo_out = hb_fifo_init(FIFO_LARGE, FIFO_LARGE_WAKE);
audio->priv.fifo_in = hb_fifo_init(FIFO_LARGE, FIFO_LARGE_WAKE);
- /* Passthru audio */
- if (audio->config.out.codec & HB_ACODEC_PASS_FLAG)
- {
- // Muxer needs these to be set correctly in order to
- // set audio track MP4 time base.
- audio->config.out.samples_per_frame =
- audio->config.in.samples_per_frame;
- audio->config.out.samplerate = audio->config.in.samplerate;
- continue;
- }
-
- /* Vorbis language information */
- if (audio->config.out.codec == HB_ACODEC_VORBIS)
- audio->priv.config.vorbis.language = audio->config.lang.simple;
-
- /* sense-check the requested samplerate */
- if (audio->config.out.samplerate <= 0)
- {
- // if not specified, set to same as input
- audio->config.out.samplerate = audio->config.in.samplerate;
- }
- best_samplerate =
- hb_audio_samplerate_get_best(audio->config.out.codec,
- audio->config.out.samplerate,
- NULL);
- if (best_samplerate != audio->config.out.samplerate)
- {
- hb_log("work: sanitizing track %d unsupported samplerate %d Hz to %s kHz",
- audio->config.out.track, audio->config.out.samplerate,
- hb_audio_samplerate_get_name(best_samplerate));
- audio->config.out.samplerate = best_samplerate;
- }
-
- /* sense-check the requested mixdown */
- if (audio->config.out.mixdown <= HB_AMIXDOWN_NONE)
- {
- /* Mixdown not specified, set the default mixdown */
- audio->config.out.mixdown =
- hb_mixdown_get_default(audio->config.out.codec,
- audio->config.in.channel_layout);
- hb_log("work: mixdown not specified, track %d setting mixdown %s",
- audio->config.out.track,
- hb_mixdown_get_name(audio->config.out.mixdown));
- }
- else
- {
- best_mixdown =
- hb_mixdown_get_best(audio->config.out.codec,
- audio->config.in.channel_layout,
- audio->config.out.mixdown);
- if (audio->config.out.mixdown != best_mixdown)
- {
- /* log the output mixdown */
- hb_log("work: sanitizing track %d mixdown %s to %s",
- audio->config.out.track,
- hb_mixdown_get_name(audio->config.out.mixdown),
- hb_mixdown_get_name(best_mixdown));
- audio->config.out.mixdown = best_mixdown;
- }
- }
-
- /* sense-check the requested compression level */
- if (audio->config.out.compression_level < 0)
+ // Add audio decoder work object
+ w = hb_audio_decoder(job->h, audio->config.in.codec);
+ if (w == NULL)
{
- audio->config.out.compression_level =
- hb_audio_compression_get_default(audio->config.out.codec);
- if (audio->config.out.compression_level >= 0)
- {
- hb_log("work: compression level not specified, track %d setting compression level %.2f",
- audio->config.out.track,
- audio->config.out.compression_level);
- }
- }
- else
- {
- float best_compression =
- hb_audio_compression_get_best(audio->config.out.codec,
- audio->config.out.compression_level);
- if (best_compression != audio->config.out.compression_level)
- {
- if (best_compression == -1)
- {
- hb_log("work: track %d, compression level not supported by codec",
- audio->config.out.track);
- }
- else
- {
- hb_log("work: sanitizing track %d compression level %.2f to %.2f",
- audio->config.out.track,
- audio->config.out.compression_level,
- best_compression);
- }
- audio->config.out.compression_level = best_compression;
- }
+ hb_error("Invalid input codec: %d", audio->config.in.codec);
+ *job->done_error = HB_ERROR_WRONG_INPUT;
+ *job->die = 1;
+ goto cleanup;
}
+ w->fifo_in = audio->priv.fifo_in;
+ w->fifo_out = audio->priv.fifo_raw;
+ w->config = &audio->priv.config;
+ w->audio = audio;
+ w->codec_param = audio->config.in.codec_param;
- /* sense-check the requested quality */
- if (audio->config.out.quality != HB_INVALID_AUDIO_QUALITY)
- {
- float best_quality =
- hb_audio_quality_get_best(audio->config.out.codec,
- audio->config.out.quality);
- if (best_quality != audio->config.out.quality)
- {
- if (best_quality == HB_INVALID_AUDIO_QUALITY)
- {
- hb_log("work: track %d, quality mode not supported by codec",
- audio->config.out.track);
- }
- else
- {
- hb_log("work: sanitizing track %d quality %.2f to %.2f",
- audio->config.out.track,
- audio->config.out.quality, best_quality);
- }
- audio->config.out.quality = best_quality;
- }
- }
-
- /* sense-check the requested bitrate */
- if (audio->config.out.quality == HB_INVALID_AUDIO_QUALITY)
- {
- if (audio->config.out.bitrate <= 0)
- {
- /* Bitrate not specified, set the default bitrate */
- audio->config.out.bitrate =
- hb_audio_bitrate_get_default(audio->config.out.codec,
- audio->config.out.samplerate,
- audio->config.out.mixdown);
- if (audio->config.out.bitrate > 0)
- {
- hb_log("work: bitrate not specified, track %d setting bitrate %d Kbps",
- audio->config.out.track,
- audio->config.out.bitrate);
- }
- }
- else
- {
- best_bitrate =
- hb_audio_bitrate_get_best(audio->config.out.codec,
- audio->config.out.bitrate,
- audio->config.out.samplerate,
- audio->config.out.mixdown);
- if (best_bitrate > 0 &&
- best_bitrate != audio->config.out.bitrate)
- {
- /* log the output bitrate */
- hb_log("work: sanitizing track %d bitrate %d to %d Kbps",
- audio->config.out.track,
- audio->config.out.bitrate, best_bitrate);
- }
- audio->config.out.bitrate = best_bitrate;
- }
- }
-
- /* sense-check the requested dither */
- if (hb_audio_dither_is_supported(audio->config.out.codec))
- {
- if (audio->config.out.dither_method ==
- hb_audio_dither_get_default())
- {
- /* "auto", enable with default settings */
- audio->config.out.dither_method =
- hb_audio_dither_get_default_method();
- }
- }
- else if (audio->config.out.dither_method !=
- hb_audio_dither_get_default())
- {
- /* specific dither requested but dithering not supported */
- hb_log("work: track %d, dithering not supported by codec",
- audio->config.out.track);
- }
+ hb_list_add( job->list_work, w );
}
}
- /* Synchronization */
- sync = hb_sync_init( job );
+ // Subtitle fifos must be initialized before sync
+ for (i = 0; i < hb_list_count( job->list_subtitle ); i++)
+ {
+ subtitle = hb_list_item( job->list_subtitle, i );
+ // Must set capacity of the raw-FIFO to be set >= the maximum
+ // number of subtitle lines that could be decoded prior to a
+ // video frame in order to prevent the following deadlock
+ // condition:
+ // 1. Subtitle decoder blocks trying to generate more subtitle
+ // lines than will fit in the FIFO.
+ // 2. Blocks the processing of further subtitle packets read
+ // from the input stream.
+ // 3. And that blocks the processing of any further video
+ // packets read from the input stream.
+ // 4. And that blocks the sync work-object from running, which
+ // is needed to consume the subtitle lines in the raw-FIFO.
+ // Since that number is unbounded, the FIFO must be made
+ // (effectively) unbounded in capacity.
+ subtitle->fifo_raw = hb_fifo_init( FIFO_UNBOUNDED, FIFO_UNBOUNDED_WAKE );
+ subtitle->fifo_in = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
+ subtitle->fifo_sync = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
+ subtitle->fifo_out = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
+
+ w = hb_get_work( job->h, subtitle->codec );
+ w->fifo_in = subtitle->fifo_in;
+ w->fifo_out = subtitle->fifo_raw;
+ w->subtitle = subtitle;
+ hb_list_add( job->list_work, w );
+ }
- /* Video decoder */
- if (title->video_codec == WORK_NONE)
+ // Video decoder
+ w = hb_video_decoder(job->h, title->video_codec, title->video_codec_param);
+ if (w == NULL)
{
- hb_error("No video decoder set!");
+ *job->done_error = HB_ERROR_WRONG_INPUT;
+ *job->die = 1;
goto cleanup;
}
- hb_list_add(job->list_work, (w = hb_get_work(job->h, title->video_codec)));
w->yield = 1; // decoders yield to keep sync fifos more even
- w->codec_param = title->video_codec_param;
w->fifo_in = job->fifo_mpeg2;
w->fifo_out = job->fifo_raw;
+ hb_list_add(job->list_work, w);
- for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
- {
- subtitle = hb_list_item( job->list_subtitle, i );
+ // Synchronization
+ w = hb_get_work(job->h, WORK_SYNC_VIDEO);
+ hb_list_add(job->list_work, w);
- if( subtitle )
+ if (!job->indepth_scan)
+ {
+ for( i = 0; i < hb_list_count( job->list_audio ); i++ )
{
- subtitle->fifo_in = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
- // Must set capacity of the raw-FIFO to be set >= the maximum number of subtitle
- // lines that could be decoded prior to a video frame in order to prevent the following
- // deadlock condition:
- // 1. Subtitle decoder blocks trying to generate more subtitle lines than will fit in the FIFO.
- // 2. Blocks the processing of further subtitle packets read from the input stream.
- // 3. And that blocks the processing of any further video packets read from the input stream.
- // 4. And that blocks the sync work-object from running, which is needed to consume the subtitle lines in the raw-FIFO.
- // Since that number is unbounded, the FIFO must be made (effectively) unbounded in capacity.
- subtitle->fifo_raw = hb_fifo_init( FIFO_UNBOUNDED, FIFO_UNBOUNDED_WAKE );
- subtitle->fifo_sync = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
- subtitle->fifo_out = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
-
- w = hb_get_work( job->h, subtitle->codec );
- w->fifo_in = subtitle->fifo_in;
- w->fifo_out = subtitle->fifo_raw;
- w->subtitle = subtitle;
- hb_list_add( job->list_work, w );
+ audio = hb_list_item( job->list_audio, i );
+
+ /*
+ * Audio Encoder Thread
+ */
+ if ( !(audio->config.out.codec & HB_ACODEC_PASS_FLAG ) )
+ {
+ /*
+ * Add the encoder thread if not doing pass through
+ */
+ w = hb_audio_encoder( job->h, audio->config.out.codec);
+ if (w == NULL)
+ {
+ hb_error("Invalid audio codec: %#x", audio->config.out.codec);
+ w = NULL;
+ *job->done_error = HB_ERROR_WRONG_INPUT;
+ *job->die = 1;
+ goto cleanup;
+ }
+ w->fifo_in = audio->priv.fifo_sync;
+ w->fifo_out = audio->priv.fifo_out;
+ w->config = &audio->priv.config;
+ w->audio = audio;
+
+ hb_list_add( job->list_work, w );
+ }
}
- }
- /* Set up the video filter fifo pipeline */
- if( !job->indepth_scan )
- {
- if( job->list_filter )
+ /* Set up the video filter fifo pipeline */
+ if ( job->list_filter )
{
- int filter_count = hb_list_count( job->list_filter );
- int i;
hb_fifo_t * fifo_in = job->fifo_sync;
-
- for( i = 0; i < filter_count; i++ )
+ for (i = 0; i < hb_list_count(job->list_filter); i++)
{
- hb_filter_object_t * filter = hb_list_item( job->list_filter, i );
+ hb_filter_object_t * filter = hb_list_item(job->list_filter, i);
filter->fifo_in = fifo_in;
filter->fifo_out = hb_fifo_init( FIFO_MINI, FIFO_MINI_WAKE );
@@ -1345,38 +1608,16 @@ static void do_job(hb_job_t *job)
job->fifo_render = NULL;
}
- /* Video encoder */
- switch( job->vcodec )
+ // Video encoder
+ w = hb_video_encoder(job->h, job->vcodec);
+ if (w == NULL)
{
- case HB_VCODEC_FFMPEG_MPEG4:
- w = hb_get_work( job->h, WORK_ENCAVCODEC );
- w->codec_param = AV_CODEC_ID_MPEG4;
- break;
- case HB_VCODEC_FFMPEG_MPEG2:
- w = hb_get_work( job->h, WORK_ENCAVCODEC );
- w->codec_param = AV_CODEC_ID_MPEG2VIDEO;
- break;
- case HB_VCODEC_FFMPEG_VP8:
- w = hb_get_work( job->h, WORK_ENCAVCODEC );
- w->codec_param = AV_CODEC_ID_VP8;
- break;
- case HB_VCODEC_X264:
- w = hb_get_work( job->h, WORK_ENCX264 );
- break;
- case HB_VCODEC_QSV_H264:
- case HB_VCODEC_QSV_H265:
- w = hb_get_work( job->h, WORK_ENCQSV );
- break;
- case HB_VCODEC_THEORA:
- w = hb_get_work( job->h, WORK_ENCTHEORA );
- break;
-#ifdef USE_X265
- case HB_VCODEC_X265:
- w = hb_get_work( job->h, WORK_ENCX265 );
- break;
-#endif
+ *job->done_error = HB_ERROR_INIT;
+ *job->die = 1;
+ goto cleanup;
}
- // Handle case where there are no filters.
+
+ // Handle case where there are no filters.
// This really should never happen.
if ( job->fifo_render )
w->fifo_in = job->fifo_render;
@@ -1388,59 +1629,17 @@ static void do_job(hb_job_t *job)
hb_list_add( job->list_work, w );
- for( i = 0; i < hb_list_count( job->list_audio ); i++ )
- {
- audio = hb_list_item( job->list_audio, i );
-
- /*
- * Audio Decoder Thread
- */
- if ( audio->priv.fifo_in )
- {
- w = hb_codec_decoder(job->h, audio->config.in.codec);
- if (w == NULL)
- {
- hb_error("Invalid input codec: %d", audio->config.in.codec);
- *job->done_error = HB_ERROR_WRONG_INPUT;
- *job->die = 1;
- goto cleanup;
- }
- w->fifo_in = audio->priv.fifo_in;
- w->fifo_out = audio->priv.fifo_raw;
- w->config = &audio->priv.config;
- w->audio = audio;
- w->codec_param = audio->config.in.codec_param;
-
- hb_list_add( job->list_work, w );
- }
-
- /*
- * Audio Encoder Thread
- */
- if ( !(audio->config.out.codec & HB_ACODEC_PASS_FLAG ) )
- {
- /*
- * Add the encoder thread if not doing AC-3 pass through
- */
- w = hb_codec_encoder( job->h, audio->config.out.codec);
- if (w == NULL)
- {
- hb_error("Invalid audio codec: %#x", audio->config.out.codec);
- w = NULL;
- *job->done_error = HB_ERROR_WRONG_INPUT;
- *job->die = 1;
- goto cleanup;
- }
- w->fifo_in = audio->priv.fifo_sync;
- w->fifo_out = audio->priv.fifo_out;
- w->config = &audio->priv.config;
- w->audio = audio;
+ }
- hb_list_add( job->list_work, w );
- }
- }
+ // Add Muxer work object
+ // Muxer work object should be the last object added to the list
+ // during regular encoding pass. For subtitle scan, sync is last.
+ if (!job->indepth_scan)
+ {
+ w = hb_get_work(job->h, WORK_MUX);
+ hb_list_add(job->list_work, w);
}
-
+
if( job->chapter_markers && job->chapter_start == job->chapter_end )
{
job->chapter_markers = 0;
@@ -1450,200 +1649,90 @@ static void do_job(hb_job_t *job)
/* Display settings */
hb_display_job_info( job );
- /* Init read & write threads */
- if ( reader->init( reader, job ) )
- {
- hb_error( "Failure to initialise thread '%s'", reader->name );
- *job->done_error = HB_ERROR_INIT;
- *job->die = 1;
- goto cleanup;
- }
- reader->done = &job->done;
- reader->thread = hb_thread_init( reader->name, ReadLoop, reader, HB_NORMAL_PRIORITY );
-
+ // Initialize all work objects
job->done = 0;
-
- if( job->list_filter && !job->indepth_scan )
- {
- int filter_count = hb_list_count( job->list_filter );
- int i;
-
- for( i = 0; i < filter_count; i++ )
- {
- hb_filter_object_t * filter = hb_list_item( job->list_filter, i );
-
- if( !filter ) continue;
-
- // Filters were initialized earlier, so we just need
- // to start the filter's thread
- filter->done = &job->done;
- filter->thread = hb_thread_init( filter->name, filter_loop, filter,
- HB_LOW_PRIORITY );
- }
- }
-
- /* Launch processing threads */
- for( i = 0; i < hb_list_count( job->list_work ); i++ )
+ for (i = 0; i < hb_list_count( job->list_work ); i++)
{
w = hb_list_item( job->list_work, i );
w->done = &job->done;
- w->thread_sleep_interval = 10;
- if( w->init( w, job ) )
+ if (w->init( w, job ))
{
hb_error( "Failure to initialise thread '%s'", w->name );
*job->done_error = HB_ERROR_INIT;
*job->die = 1;
goto cleanup;
}
- w->thread = hb_thread_init( w->name, work_loop, w,
- HB_LOW_PRIORITY );
}
- if ( job->indepth_scan )
- {
- muxer = NULL;
- w = sync;
- sync->done = &job->done;
- }
- else
+ /* Launch processing threads */
+ for (i = 0; i < hb_list_count( job->list_work ); i++)
{
- sync->done = &job->done;
- sync->thread_sleep_interval = 10;
- if( sync->init( w, job ) )
- {
- hb_error( "Failure to initialise thread '%s'", w->name );
- *job->done_error = HB_ERROR_INIT;
- *job->die = 1;
- goto cleanup;
- }
- sync->thread = hb_thread_init( sync->name, work_loop, sync,
- HB_LOW_PRIORITY );
-
- // The muxer requires track information that's set up by the encoder
- // init routines so we have to init the muxer last.
- muxer = hb_muxer_init( job );
- w = muxer;
+ w = hb_list_item(job->list_work, i);
+ w->thread = hb_thread_init(w->name, hb_work_loop, w, HB_LOW_PRIORITY);
}
-
- hb_buffer_t * buf_in, * buf_out = NULL;
-
- while ( !*job->die && !*w->done && w->status != HB_WORK_DONE )
+ if (job->list_filter && !job->indepth_scan)
{
- buf_in = hb_fifo_get_wait( w->fifo_in );
- if ( buf_in == NULL )
- continue;
- if ( *job->die )
+ for (i = 0; i < hb_list_count(job->list_filter); i++)
{
- if( buf_in )
- {
- hb_buffer_close( &buf_in );
- }
- break;
- }
-
- buf_out = NULL;
- w->status = w->work( w, &buf_in, &buf_out );
+ hb_filter_object_t * filter = hb_list_item(job->list_filter, i);
- if( buf_in )
- {
- hb_buffer_close( &buf_in );
- }
- if ( buf_out && w->fifo_out == NULL )
- {
- hb_buffer_close( &buf_out );
- }
- if( buf_out )
- {
- while ( !*job->die )
- {
- if ( hb_fifo_full_wait( w->fifo_out ) )
- {
- hb_fifo_push( w->fifo_out, buf_out );
- buf_out = NULL;
- break;
- }
- }
+ // Filters were initialized earlier, so we just need
+ // to start the filter's thread
+ filter->thread = hb_thread_init( filter->name, filter_loop, filter,
+ HB_LOW_PRIORITY );
}
}
- if ( buf_out )
- {
- hb_buffer_close( &buf_out );
- }
+ // Wait for the thread of the last work object to complete
+ w = hb_list_item(job->list_work, hb_list_count(job->list_work) - 1);
+ w->die = job->die;
+ hb_thread_close(&w->thread);
hb_handle_t * h = job->h;
hb_state_t state;
hb_get_state2( h, &state );
- hb_log("work: average encoding speed for job is %f fps", state.param.working.rate_avg);
-
- job->done = 1;
- if( muxer != NULL )
- {
- muxer->close( muxer );
- free( muxer );
-
- if( sync->thread != NULL )
- {
- hb_thread_close( &sync->thread );
- sync->close( sync );
- }
- free( sync );
- }
+ hb_log("work: average encoding speed for job is %f fps",
+ state.param.working.rate_avg);
cleanup:
- /* Stop the write thread (thread_close will block until the muxer finishes) */
job->done = 1;
// Close render filter pipeline
- if( job->list_filter )
+ if (job->list_filter)
{
- int filter_count = hb_list_count( job->list_filter );
- int i;
-
- for( i = 0; i < filter_count; i++ )
+ for (i = 0; i < hb_list_count(job->list_filter); i++)
{
- hb_filter_object_t * filter = hb_list_item( job->list_filter, i );
-
- if( !filter ) continue;
-
+ hb_filter_object_t * filter = hb_list_item(job->list_filter, i);
if( filter->thread != NULL )
{
- hb_thread_close( &filter->thread );
+ hb_thread_close(&filter->thread);
}
- filter->close( filter );
+ filter->close(filter);
}
}
/* Close work objects */
- while( ( w = hb_list_item( job->list_work, 0 ) ) )
+ while ((w = hb_list_item(job->list_work, 0)))
{
- hb_list_rem( job->list_work, w );
- if( w->thread != NULL )
+ hb_list_rem(job->list_work, w);
+ if (w->thread != NULL)
{
- hb_thread_close( &w->thread );
- w->close( w );
+ hb_thread_close(&w->thread);
}
- free( w );
+ w->close(w);
+ free(w);
}
hb_list_close( &job->list_work );
- /* Stop the read thread */
- if( reader->thread != NULL )
- {
- hb_thread_close( &reader->thread );
- reader->close( reader );
- }
- free( reader );
-
/* Close fifos */
hb_fifo_close( &job->fifo_mpeg2 );
hb_fifo_close( &job->fifo_raw );
hb_fifo_close( &job->fifo_sync );
hb_fifo_close( &job->fifo_mpeg4 );
- for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
+ for (i = 0; i < hb_list_count( job->list_subtitle ); i++)
{
subtitle = hb_list_item( job->list_subtitle, i );
if( subtitle )
@@ -1654,7 +1743,7 @@ cleanup:
hb_fifo_close( &subtitle->fifo_out );
}
}
- for( i = 0; i < hb_list_count( job->list_audio ); i++ )
+ for (i = 0; i < hb_list_count( job->list_audio ); i++)
{
audio = hb_list_item( job->list_audio, i );
if( audio->priv.fifo_in != NULL )
@@ -1667,94 +1756,22 @@ cleanup:
hb_fifo_close( &audio->priv.fifo_out );
}
- if( job->list_filter )
+ if (job->list_filter)
{
- for( i = 0; i < hb_list_count( job->list_filter ); i++ )
+ for (i = 0; i < hb_list_count( job->list_filter ); i++)
{
hb_filter_object_t * filter = hb_list_item( job->list_filter, i );
hb_fifo_close( &filter->fifo_out );
}
}
- if( job->indepth_scan )
+ if (job->indepth_scan)
{
- /* Before closing the title print out our subtitle stats if we need to
- * find the highest and lowest. */
- for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
- {
- subtitle = hb_list_item( job->list_subtitle, i );
-
- hb_log( "Subtitle track %d (id 0x%x) '%s': %d hits (%d forced)",
- subtitle->track, subtitle->id, subtitle->lang,
- subtitle->hits, subtitle->forced_hits );
-
- if( subtitle->hits == 0 )
- continue;
-
- if( subtitle_highest < subtitle->hits )
- {
- subtitle_highest = subtitle->hits;
- }
-
- if( subtitle_lowest == 0 ||
- subtitle_lowest > subtitle->hits )
- {
- subtitle_lowest = subtitle->hits;
- subtitle_lowest_id = subtitle->id;
- }
-
- // pick the track with fewest forced hits
- if( subtitle->forced_hits > 0 &&
- ( subtitle_forced_hits == 0 ||
- subtitle_forced_hits > subtitle->forced_hits ) )
- {
- subtitle_forced_id = subtitle->id;
- subtitle_forced_hits = subtitle->forced_hits;
- }
- }
-
- if( subtitle_forced_id && job->select_subtitle_config.force )
- {
- /* If there is a subtitle stream with forced subtitles and forced-only
- * is set, then select it in preference to the lowest. */
- subtitle_hit = subtitle_forced_id;
- hb_log( "Found a subtitle candidate with id 0x%x (contains forced subs)",
- subtitle_hit );
- }
- else if( subtitle_lowest > 0 &&
- subtitle_lowest < ( subtitle_highest * 0.1 ) )
- {
- /* OK we have more than one, and the lowest is lower,
- * but how much lower to qualify for turning it on by
- * default?
- *
- * Let's say 10% as a default. */
- subtitle_hit = subtitle_lowest_id;
- hb_log( "Found a subtitle candidate with id 0x%x", subtitle_hit );
- }
- else
- {
- hb_log( "No candidate detected during subtitle scan" );
- }
-
- for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
- {
- subtitle = hb_list_item( job->list_subtitle, i );
- if( subtitle->id == subtitle_hit )
- {
- subtitle->config = job->select_subtitle_config;
- // Remove from list since we are taking ownership
- // of the subtitle.
- hb_list_rem( job->list_subtitle, subtitle );
- interjob->select_subtitle = subtitle;
- break;
- }
- }
+ analyze_subtitle_scan(job);
}
hb_buffer_pool_free();
-
- hb_job_close( &job );
+ hb_job_close(&job);
}
static inline void copy_chapter( hb_buffer_t * dst, hb_buffer_t * src )
@@ -1778,23 +1795,28 @@ static inline void copy_chapter( hb_buffer_t * dst, hb_buffer_t * src )
* Exits loop when work indiactor is set.
* @param _w Handle to work object.
*/
-static void work_loop( void * _w )
+void hb_work_loop( void * _w )
{
hb_work_object_t * w = _w;
hb_buffer_t * buf_in = NULL, * buf_out = NULL;
- while( !*w->done && w->status != HB_WORK_DONE )
+ while ((w->die == NULL || !*w->die) && !*w->done &&
+ w->status != HB_WORK_DONE)
{
- buf_in = hb_fifo_get_wait( w->fifo_in );
- if ( buf_in == NULL )
- continue;
- if ( *w->done )
+ // fifo_in == NULL means this is a data source (e.g. reader)
+ if (w->fifo_in != NULL)
{
- if( buf_in )
+ buf_in = hb_fifo_get_wait( w->fifo_in );
+ if ( buf_in == NULL )
+ continue;
+ if ( *w->done )
{
- hb_buffer_close( &buf_in );
+ if( buf_in )
+ {
+ hb_buffer_close( &buf_in );
+ }
+ break;
}
- break;
}
// Invalidate buf_out so that if there is no output
// we don't try to pass along junk.
@@ -1832,20 +1854,11 @@ static void work_loop( void * _w )
{
hb_buffer_close( &buf_out );
}
-
- // Consume data in incoming fifo till job complete so that
- // residual data does not stall the pipeline
- while( !*w->done )
- {
- buf_in = hb_fifo_get_wait( w->fifo_in );
- if ( buf_in != NULL )
- hb_buffer_close( &buf_in );
- }
}
/**
* Performs the filter object's specific work function.
- * Loops calling work function for associated filter object.
+ * Loops calling work function for associated filter object.
* Sleeps when fifo is full.
* Monitors work done indicator.
* Exits loop when work indiactor is set.