Index: th-job.c =================================================================== RCS file: /cvsroot/thoggen/thoggen/src/th-job.c,v retrieving revision 1.35 diff -u -p -r1.35 th-job.c --- th-job.c 16 Mar 2005 16:00:23 -0000 1.35 +++ th-job.c 9 Apr 2006 19:26:37 -0000 @@ -84,6 +84,13 @@ enum LAST_SIGNAL }; + +typedef struct _ThJobEOSWatchHelper +{ + GstClockTime duration; + gint sent_eos; /* access atomically */ +} ThJobEOSWatchHelper; + struct _ThJobPrivate { gchar *device; /* device */ @@ -141,6 +148,9 @@ struct _ThJobPrivate /* tag */ gchar *title_tag; gchar *comment_tag; + + ThJobEOSWatchHelper audio_watch; + ThJobEOSWatchHelper video_watch; }; typedef struct _ThJobAudioStream ThJobAudioStream; @@ -1655,6 +1665,27 @@ job_probe_pads (ThJob *j, GError **err) /*************************************************************************** * + * job_check_eos_watches + * + ***************************************************************************/ + +static gboolean +job_check_eos_watches (ThJob *j) +{ + if (g_atomic_int_get (&j->priv->audio_watch.sent_eos) && + g_atomic_int_get (&j->priv->video_watch.sent_eos)) { + th_log ("Both EOS watches sent EOS!"); + job_eos_cb (j, j->priv->pipeline); + /* wait for a second to give elements a chance + * to process the EOS and the remaining data */ + g_usleep (G_USEC_PER_SEC); + } + + return TRUE; +} + +/*************************************************************************** + * * job_check_free_disk_space * ***************************************************************************/ @@ -1838,6 +1869,107 @@ job_create_pipeline_setup_video_encoder /*************************************************************************** * + * job_eos_probe_cb + * + * Called whenever data (a buffer or an event) leaves the decoder. When + * we find it pushes buffers beyond the end of the file, we suppress them + * and send an EOS event instead, hoping that will hack around the problem + * with ripping sometimes not stopping at the end of a title. + * + ***************************************************************************/ + +static gboolean +job_eos_probe_cb (GstProbe *p, GstData **p_data, ThJobEOSWatchHelper *helper) +{ + GstClockTime ts; + + if (g_atomic_int_get (&helper->sent_eos) != 0) + return FALSE; /* remove data from stream */ + + if (GST_IS_BUFFER (*p_data)) { + ts = GST_BUFFER_TIMESTAMP (*p_data); + goto check_timestamp; + } + + if (GST_IS_EVENT (*p_data)) { + switch (GST_EVENT_TYPE (*p_data)) + { + case GST_EVENT_FILLER: + { + guint64 dur; + + ts = GST_EVENT_TIMESTAMP (*p_data); + if (ts == (guint64) -1) + break; + dur = gst_event_filler_get_duration (GST_EVENT (*p_data)); + if (dur == (guint64) -1) + break; + ts += dur; + goto check_timestamp; + } + case GST_EVENT_EOS: + { + g_atomic_int_inc (&helper->sent_eos); + break; + } + default: + break; + } + } + + return TRUE; /* do not remove data from stream */ + + +check_timestamp: + { + if (ts == (guint64) -1 || ts <= helper->duration) + return TRUE; /* do not remove data from stream */ + + /* remove data if timestamp is beyond duration */ + th_log ("Injecting artificial EOS"); + g_atomic_int_inc (&helper->sent_eos); + gst_data_unref (*p_data); + *p_data = GST_DATA (gst_event_new (GST_EVENT_EOS)); + return TRUE; /* send EOS event downstream */ + } +} + +/*************************************************************************** + * + * job_create_pipeline_setup_eos_watch + * + ***************************************************************************/ + +static void +job_create_pipeline_setup_eos_watch (ThJob *j, const gchar *decoder_name, + ThJobEOSWatchHelper * helper) +{ + GstElement *decoder; + GstProbe *decoder_probe; + GstPad *src_pad; + + decoder = gst_bin_get_by_name (GST_BIN (j->priv->pipeline), decoder_name); + g_return_if_fail (decoder != NULL); + + src_pad = gst_element_get_pad (decoder, "src"); + g_return_if_fail (GST_IS_PAD (src_pad)); + + decoder_probe = gst_probe_new (FALSE, + (GstProbeCallback) job_eos_probe_cb, + helper); + + gst_pad_add_probe (src_pad, decoder_probe); + + g_object_weak_ref (G_OBJECT (src_pad), + (GWeakNotify) gst_probe_destroy, + decoder_probe); + + helper->duration = GST_SECOND * j->priv->title_length; + helper->sent_eos = FALSE; +} + +/*************************************************************************** + * * job_create_pipeline * ***************************************************************************/ @@ -1868,7 +2000,7 @@ job_create_pipeline (ThJob *j, GError ** " .%s " " ! { " " queue name=q-a-in " - " ! %s " + " ! %s name=audio-decoder " " ! audioconvert " " ! audioscale " " ! audio/x-raw-int,rate=44100,channels=2 " @@ -1880,7 +2012,7 @@ job_create_pipeline (ThJob *j, GError ** " { " " demux.%s " " ! queue name=q-v-in " - " ! mpeg2dec " + " ! mpeg2dec name=video-decoder " " ! videorate " " ! video/x-raw-yuv,framerate=(double)%u.0 " " ! videocrop left=%u right=%u top=%u bottom=%u " @@ -1955,6 +2087,12 @@ job_create_pipeline (ThJob *j, GError ** "handoff", G_CALLBACK (job_output_identity_handoff_cb), j); + + /* set up EOS watches */ + job_create_pipeline_setup_eos_watch (j, "audio-decoder", + &j->priv->audio_watch); + job_create_pipeline_setup_eos_watch (j, "video-decoder", + &j->priv->video_watch); gst_element_set_state (j->priv->pipeline, GST_STATE_PLAYING); @@ -1975,7 +2113,7 @@ job_create_pipeline (ThJob *j, GError ** gboolean th_job_run (ThJob *j, GError **err) { - gulong checkid; + gulong checkid, eos_checkid; g_return_val_if_fail (TH_IS_JOB (j), FALSE); g_return_val_if_fail (j->priv->pipeline == NULL, FALSE); @@ -2008,10 +2146,15 @@ th_job_run (ThJob *j, GError **err) checkid = g_timeout_add (DISK_SPACE_CHECK_INTERVAL*1000, (GSourceFunc) job_check_free_disk_space, j); + + eos_checkid = g_timeout_add (5*1000, + (GSourceFunc) job_check_eos_watches, + j); g_main_loop_run (j->priv->loop); g_source_remove (checkid); + g_source_remove (eos_checkid); if (j->priv->pipeline) {