From 9920b88c3f1473ce2d9ef5794c4ef9059d9b8d3f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 12 Jul 2000 22:52:42 +0000 Subject: [PATCH] This is the audio/video sync release. Original commit message from CVS: This is the audio/video sync release. Changed the mpegvideoparser to parse complete pictures. Added the PTS timestamps to the pictures. Added PTS timestamps to the MPEG audio frames. Made the clock a littlebit better. Gstplay now uses two more threads one for video, one for audio playback. Added the first QoS callbacks for the pads. hopefully fix an mmx compilation problem. --- configure.in | 26 ++++----- docs/random/states.new | 79 +++++++++++++++++++++++++++ gst/elements/gstaudiosink.c | 38 +++++++------ gst/elements/gstaudiosink.h | 2 +- gst/elements/gstqueue.c | 43 ++++++++++----- gst/elements/gstqueue.h | 2 +- gst/gst.h | 6 ++- gst/gstbin.c | 23 ++++---- gst/gstbuffer.h | 3 +- gst/gstclock.c | 94 ++++++++++++++++----------------- gst/gstclock.h | 2 + gst/gstelement.c | 7 ++- gst/gstelement.h | 4 +- gst/gstpad.c | 63 ++++++++++++++++++---- gst/gstpad.h | 4 ++ gst/gstpipeline.c | 7 +-- gst/gstthread.c | 38 +++++++------ gstplay/callbacks.c | 6 +++ gstplay/gstplay.c | 88 ++++++++++++++++++++++++++---- gstplay/mpeg1.c | 28 +++++----- include/mmx.h | 8 +-- plugins/elements/gstaudiosink.c | 38 +++++++------ plugins/elements/gstaudiosink.h | 2 +- plugins/elements/gstqueue.c | 43 ++++++++++----- plugins/elements/gstqueue.h | 2 +- 25 files changed, 450 insertions(+), 206 deletions(-) create mode 100644 docs/random/states.new diff --git a/configure.in b/configure.in index d3835c47ee..a27c3e87b0 100644 --- a/configure.in +++ b/configure.in @@ -101,19 +101,6 @@ AC_CHECK_LIB(ghttp, ghttp_request_new, AC_SUBST(GHTTP_LIBS) AC_SUBST(GST_HTTPSRC_GET_TYPE) -dnl Check for X11 extensions -AC_PATH_XTRA -if test "-DX_DISPLAY_MISSING" = "$X_CFLAGS"; then - AC_MSG_ERROR(can not find X11) -fi -AC_SUBST(X_CFLAGS) -AC_SUBST(X_PRE_LIBS) -AC_SUBST(X_EXTRA_LIBS) -AC_SUBST(X_LIBS) - -AC_CHECK_LIB(Xv, XvQueryExtension,,, - $X_LIBS $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS) - dnl Check for atomic.h dnl Note: use AC_CHECK_HEADER not AC_CHECK_HEADERS, because the latter dnl defines the wrong default symbol as well (HAVE_ASM_ATOMIC_H) @@ -155,6 +142,19 @@ HAVE_LIBMMX="no" AC_MSG_RESULT(no) ) +dnl Check for X11 extensions +AC_PATH_XTRA +if test "-DX_DISPLAY_MISSING" = "$X_CFLAGS"; then + AC_MSG_ERROR(can not find X11) +fi +AC_SUBST(X_CFLAGS) +AC_SUBST(X_PRE_LIBS) +AC_SUBST(X_EXTRA_LIBS) +AC_SUBST(X_LIBS) + +AC_CHECK_LIB(Xv, XvQueryExtension,,, + $X_LIBS $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS) + dnl Check for xaudio AC_CHECK_HEADER(xaudio/decoder.h,[ AC_DEFINE(HAVE_XAUDIO) diff --git a/docs/random/states.new b/docs/random/states.new new file mode 100644 index 0000000000..1df4e724de --- /dev/null +++ b/docs/random/states.new @@ -0,0 +1,79 @@ +Since the plan generation only happens as a result of the state mechanism, +I'll describe that first. + +It's supposed to be recursive, such that setting the state on a Bin +recursively sets all the children. However, this needs to be rethought +somewhat, in light of some recent ideas on the actual definition of some +of the states. + +The mechanism is thus: When you call gst_element_set_state(element,state), +it calls the change_state() class method. The basic Element-provided +version just sets or unsets the state. A more complex element like the +audiosink will switch on the state and do certain things like open or +close the sound card on transition to/from various states. The success or +failure of these actions can determine whether or not the state gets +[un]set as requested. + +GtkObject signals enter in here, as whenever a state is successfully +changed, the STATE_CHANGE signal is fired, which gives higher-level code +the ability to do something based on the change. + +The Bin's change_state function walks through all its children and sets +their state. This is where things get interesting, and where things are +going to need to be changed. + +The issue is what the states are and mean. Currently the states are as +follows (from gstelement.h): + +typedef enum { + GST_STATE_COMPLETE = (1 << 0), + GST_STATE_RUNNING = (1 << 1), + GST_STATE_DISCOVERY = (1 << 2), + GST_STATE_PREROLL = (1 << 3), + + GST_STATE_PLAYING = (1 << 4), + GST_STATE_PAUSED = (1 << 5), + + GST_STATE_MAX = (1 << 15), +} GstElementState; + +COMPLETE means all the necesary information is available to run, i.e. the +filename for the disksrc, etc. RUNNING means that it's actually doing +something, but that's fuzzy. PLAYING means there really is data flowing +through the graph, where PAUSED temporary stops the flow. PLAYING && +PAUSED is the same idea as !PLAYING, but there are probably going to be +many cases where there really is a distinction. + +DISCOVERY is intended for the autoconnect case, in those instances where +the only way to determine the input or output type of some pad is for an +element to actually process some data. The idea in that case is that the +source element would be responsible for sending the data non-destructively +(in the case of a network client, it would have to save it all up, unless +it has seek capabilities over the network), and all downstream elements +process it in such a way as to not hose their own state. Or rather, when +they cease to do discovery, they completely wipe their state as if nothing +ever happened. + +PREROLL is a local state, used for things like sending the first half of +an MPEG GOP through the decoder in order to start playback at a frame +somewhere in the middle of said GOP. Not sure how that will work, +exactly. + + +The issue is that these states aren't layered, and it most certainly isn't +the case that a container isn't able to be of a certain state unless all +of its children are. I guess I should explain the idea of reconfigurable +pipelines: + +Build an MP3 player, give it the ability to use audio effects plugins. +Since you don't want to have to start the stream over again (especially if +it's a network stream) every time you change the effect. This means you +need to be able to freeze the pipeline in place to change it, without +taking too much time. + +This matters when you consider that certain state changes should render +various state bits invalid. In the FROZEN state these won't happen, +because the assumption is that they're temporary. + +If you haven't noticed by now, the state system isn't entirely +self-consistent yet. It needs work, and it needs discussion. diff --git a/gst/elements/gstaudiosink.c b/gst/elements/gstaudiosink.c index b136419e4f..6c0a24950f 100644 --- a/gst/elements/gstaudiosink.c +++ b/gst/elements/gstaudiosink.c @@ -147,9 +147,8 @@ static void gst_audiosink_init(GstAudioSink *audiosink) { audiosink->fd = -1; audiosink->clock = gst_clock_get_system(); gst_clock_register(audiosink->clock, GST_OBJECT(audiosink)); - audiosink->clocktime = 0LL; + //audiosink->clocktime = 0LL; - gst_element_set_state(GST_ELEMENT(audiosink),GST_STATE_COMPLETE); } void gst_audiosink_sync_parms(GstAudioSink *audiosink) { @@ -173,19 +172,20 @@ void gst_audiosink_sync_parms(GstAudioSink *audiosink) { audiosink->frequency,audiosink->format, (audiosink->channels == 2) ? "stereo" : "mono",ospace.bytes, frag); - } GstElement *gst_audiosink_new(gchar *name) { GstElement *audiosink = GST_ELEMENT(gtk_type_new(GST_TYPE_AUDIOSINK)); gst_element_set_name(GST_ELEMENT(audiosink),name); + gst_element_set_state(GST_ELEMENT(audiosink),GST_STATE_COMPLETE); return audiosink; } void gst_audiosink_chain(GstPad *pad,GstBuffer *buf) { GstAudioSink *audiosink; MetaAudioRaw *meta; - count_info info; + gboolean in_flush; + audio_buf_info ospace; g_return_if_fail(pad != NULL); g_return_if_fail(GST_IS_PAD(pad)); @@ -197,6 +197,12 @@ void gst_audiosink_chain(GstPad *pad,GstBuffer *buf) { audiosink = GST_AUDIOSINK(pad->parent); // g_return_if_fail(GST_FLAG_IS_SET(audiosink,GST_STATE_RUNNING)); + if (in_flush = GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLUSH)) { + DEBUG("audiosink: flush\n"); + ioctl(audiosink->fd,SNDCTL_DSP_RESET,0); + } + + meta = (MetaAudioRaw *)gst_buffer_get_first_meta(buf); if (meta != NULL) { if ((meta->format != audiosink->format) || @@ -217,20 +223,20 @@ void gst_audiosink_chain(GstPad *pad,GstBuffer *buf) { gst_trace_add_entry(NULL,0,buf,"audiosink: writing to soundcard"); //g_print("audiosink: writing to soundcard\n"); if (audiosink->fd > 2) { - if (audiosink->clocktime == 0LL) - gst_clock_wait(audiosink->clock, audiosink->clocktime, GST_OBJECT(audiosink)); - ioctl(audiosink->fd,SNDCTL_DSP_GETOPTR,&info); - audiosink->clocktime = (info.bytes*1000000LL)/(audiosink->frequency*audiosink->channels); - //g_print("audiosink: bytes sent %d time %llu\n", info.bytes, audiosink->clocktime); - gst_clock_set(audiosink->clock, audiosink->clocktime); - if (!audiosink->mute) - write(audiosink->fd,GST_BUFFER_DATA(buf),GST_BUFFER_SIZE(buf)); - //audiosink->clocktime += (1000000LL*GST_BUFFER_SIZE(buf)/(audiosink->channels* -// (audiosink->format/8)*(audiosink->frequency))); - //g_print("audiosink: writing to soundcard ok\n"); + if (!audiosink->mute) { + if (gst_clock_current_diff(audiosink->clock, GST_BUFFER_TIMESTAMP(buf)) > 500000) { + } + else { + gst_clock_wait(audiosink->clock, GST_BUFFER_TIMESTAMP(buf), GST_OBJECT(audiosink)); + ioctl(audiosink->fd,SNDCTL_DSP_GETOSPACE,&ospace); + DEBUG("audiosink: (%d bytes buffer)\n", ospace.bytes); + write(audiosink->fd,GST_BUFFER_DATA(buf),GST_BUFFER_SIZE(buf)); + //gst_clock_set(audiosink->clock, GST_BUFFER_TIMESTAMP(buf)); + } + } } } - +end: //g_print("a unref\n"); gst_buffer_unref(buf); //g_print("a done\n"); diff --git a/gst/elements/gstaudiosink.h b/gst/elements/gstaudiosink.h index ecd441621c..26a1041032 100644 --- a/gst/elements/gstaudiosink.h +++ b/gst/elements/gstaudiosink.h @@ -54,7 +54,7 @@ struct _GstAudioSink { GstPad *sinkpad; - GstClockTime clocktime; + //GstClockTime clocktime; GstClock *clock; /* soundcard state */ int fd; diff --git a/gst/elements/gstqueue.c b/gst/elements/gstqueue.c index f383eb2c65..159d88d478 100644 --- a/gst/elements/gstqueue.c +++ b/gst/elements/gstqueue.c @@ -20,7 +20,7 @@ //#define DEBUG_ENABLED //#define STATUS_ENABLED #ifdef STATUS_ENABLED -#define STATUS(A) g_print(A) +#define STATUS(A) DEBUG(A, gst_element_get_name(GST_ELEMENT(queue))) #else #define STATUS(A) #endif @@ -144,6 +144,12 @@ static GstBuffer *gst_queue_pull(GstPad *pad) { else return NULL; } +static void gst_queue_cleanup_buffers(gpointer data, gpointer user_data) +{ + DEBUG("queue: %s cleaning buffer %p\n", (gchar *)user_data, data); + gst_buffer_unref(GST_BUFFER(data)); +} + void gst_queue_chain(GstPad *pad,GstBuffer *buf) { GstQueue *queue; gboolean tosignal = FALSE; @@ -157,8 +163,19 @@ void gst_queue_chain(GstPad *pad,GstBuffer *buf) { name = gst_element_get_name(GST_ELEMENT(queue)); /* we have to lock the queue since we span threads */ + + DEBUG("queue: %s adding buffer %p\n", name, buf); GST_LOCK(queue); + + if (GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLUSH)) { + g_list_foreach(queue->queue, gst_queue_cleanup_buffers, name); + g_list_free(queue->queue); + queue->queue = NULL; + queue->level_buffers = 0; + } + + DEBUG("queue: %s: chain %d %p\n", name, queue->level_buffers, buf); if (queue->level_buffers >= queue->max_buffers) { @@ -166,7 +183,7 @@ void gst_queue_chain(GstPad *pad,GstBuffer *buf) { while (queue->level_buffers >= queue->max_buffers) { GST_UNLOCK(queue); g_mutex_lock(queue->fulllock); - STATUS("O"); + STATUS("%s: O\n"); g_cond_wait(queue->fullcond,queue->fulllock); g_mutex_unlock(queue->fulllock); GST_LOCK(queue); @@ -186,7 +203,7 @@ void gst_queue_chain(GstPad *pad,GstBuffer *buf) { // queue->tail = g_list_next(queue->tail); queue->queue = g_list_append(queue->queue,buf); } - STATUS("+"); + STATUS("%s: +\n"); /* if we were empty, but aren't any more, signal a condition */ tosignal = (queue->level_buffers <= 0); @@ -213,19 +230,17 @@ void gst_queue_push(GstConnection *connection) { name = gst_element_get_name(GST_ELEMENT(queue)); + DEBUG("queue: %s push %d\n", name, queue->level_buffers); /* have to lock for thread-safety */ GST_LOCK(queue); - DEBUG("queue: %s push %d\n", name, queue->level_buffers); - if (!queue->level_buffers) { - while (!queue->level_buffers) { - GST_UNLOCK(queue); - g_mutex_lock(queue->emptylock); - STATUS("U"); - g_cond_wait(queue->emptycond,queue->emptylock); - g_mutex_unlock(queue->emptylock); - GST_LOCK(queue); - } + while (!queue->level_buffers) { + GST_UNLOCK(queue); + g_mutex_lock(queue->emptylock); + STATUS("%s: U\n"); + g_cond_wait(queue->emptycond,queue->emptylock); + g_mutex_unlock(queue->emptylock); + GST_LOCK(queue); } front = queue->queue; @@ -233,7 +248,7 @@ void gst_queue_push(GstConnection *connection) { queue->queue = g_list_remove_link(queue->queue,front); g_list_free(front); queue->level_buffers--; - STATUS("-"); + STATUS("%s: -\n"); tosignal = queue->level_buffers < queue->max_buffers; GST_UNLOCK(queue); diff --git a/gst/elements/gstqueue.h b/gst/elements/gstqueue.h index 4016b94005..bb9c47bf9e 100644 --- a/gst/elements/gstqueue.h +++ b/gst/elements/gstqueue.h @@ -43,7 +43,7 @@ GstElementDetails gst_queue_details; #define GST_IS_QUEUE(obj) \ (GTK_CHECK_TYPE((obj),GST_TYPE_QUEUE)) #define GST_IS_QUEUE_CLASS(obj) \ - (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_QUEUE))) + (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_QUEUE)) typedef struct _GstQueue GstQueue; typedef struct _GstQueueClass GstQueueClass; diff --git a/gst/gst.h b/gst/gst.h index d2e9669b3e..1f08293508 100644 --- a/gst/gst.h +++ b/gst/gst.h @@ -21,6 +21,8 @@ #ifndef __GST_H__ #define __GST_H__ +#include + #include #include @@ -51,9 +53,9 @@ void gst_init(int *argc,char **argv[]); /* debugging */ #ifndef DEBUG #ifdef DEBUG_ENABLED -#define DEBUG(format,args...) g_print("DEBUG: " format, ##args) +#define DEBUG(format, args...) g_print("DEBUG:(%d) " format, getpid() , ##args) #else -#define DEBUG(format,args...) +#define DEBUG(format, args...) #endif #endif diff --git a/gst/gstbin.c b/gst/gstbin.c index 0ea397ae7a..b3093b5272 100644 --- a/gst/gstbin.c +++ b/gst/gstbin.c @@ -151,7 +151,7 @@ void gst_bin_add(GstBin *bin,GstElement *element) { the children are, I think. */ // if (GST_STATE_IS_SET(element,GST_STATE_COMPLETE)) { if (!GST_STATE_IS_SET(bin,GST_STATE_COMPLETE)) { - g_print("GstBin: adding complete element - "); + g_print("GstBin: adding complete element - \n"); gst_bin_change_state_norecurse(GST_ELEMENT(bin),GST_STATE_COMPLETE); } // } else { @@ -199,10 +199,10 @@ static gboolean gst_bin_change_state(GstElement *element, children = bin->children; while (children) { child = GST_ELEMENT(children->data); -// g_print("gst_bin_change_state setting state on \"%s\"\n", -// gst_object_get_name(GST_OBJECT(child))); + //g_print("gst_bin_change_state setting state on \"%s\"\n", + // gst_element_get_name(GST_ELEMENT(child))); if (!gst_element_set_state(child,state)) { - g_print("child %p failed to set state 0x%08x\n",child,state); + g_print("GstBin: child %p failed to set state 0x%08x\n",child,state); return FALSE; } // g_print("\n"); @@ -255,7 +255,6 @@ static gboolean gst_bin_change_state_type(GstBin *bin, // g_print("\n"); children = g_list_next(children); } -// g_print("<-- \"%s\"\n",gst_object_get_name(GST_OBJECT(bin))); if (type == GST_TYPE_BIN) gst_element_change_state(GST_ELEMENT(bin),state); @@ -394,7 +393,7 @@ static void gst_bin_create_plan_func(GstBin *bin) { bin->numentries = 0; - g_print("attempting to create a plan for bin %p\n",bin); + g_print("GstBin: attempting to create a plan for bin %p\n",bin); /* walk through all the elements to figure out all kinds of things */ elements = GST_BIN(bin)->children; @@ -404,11 +403,11 @@ static void gst_bin_create_plan_func(GstBin *bin) { // have to use cothreads if any elements use loop functions if (element->loopfunc != NULL) { if (bin->threadcontext == NULL) { - g_print("initializing cothread context\n"); + g_print("GstBin: initializing cothread context\n"); bin->threadcontext = cothread_init(); } if (element->threadstate == NULL) { - g_print("creating thread state for element\n"); + g_print("GstBin: creating thread state for element\n"); element->threadstate = cothread_create(bin->threadcontext); cothread_setfunc(element->threadstate,gst_element_loopfunc_wrapper, 0,element); @@ -417,7 +416,7 @@ static void gst_bin_create_plan_func(GstBin *bin) { /* we need to find all the entry points into the bin */ if (GST_IS_SRC(element)) { - g_print("element '%s' is a source entry point for the bin\n", + g_print("GstBin: element '%s' is a source entry point for the bin\n", gst_element_get_name(GST_ELEMENT(element))); bin->entries = g_list_prepend(bin->entries,element); bin->numentries++; @@ -437,7 +436,7 @@ static void gst_bin_create_plan_func(GstBin *bin) { /* if it's a connection and it's not ours... */ if (GST_IS_CONNECTION(outside) && (gst_object_get_parent(GST_OBJECT(outside)) != GST_OBJECT(bin))) { - g_print("element '%s' is the external source Connection \ + g_print("GstBin: element '%s' is the external source Connection \ for internal element '%s'\n", gst_element_get_name(GST_ELEMENT(outside)), gst_element_get_name(GST_ELEMENT(element))); @@ -450,7 +449,7 @@ for internal element '%s'\n", } elements = g_list_next(elements); } - g_print("have %d entries into bin\n",bin->numentries); + g_print("GstBin: have %d entries into bin\n",bin->numentries); } void gst_bin_iterate_func(GstBin *bin) { @@ -464,7 +463,7 @@ void gst_bin_iterate_func(GstBin *bin) { entries = bin->entries; - g_print("iterating\n"); + g_print("GstBin: iterating\n"); while (entries) { entry = GST_ELEMENT(entries->data); diff --git a/gst/gstbuffer.h b/gst/gstbuffer.h index 1255301f80..c6bcaed298 100644 --- a/gst/gstbuffer.h +++ b/gst/gstbuffer.h @@ -30,8 +30,7 @@ extern "C" { #define GST_BUFFER(buf) \ - ((GstBuffer *)(buf)) - + ((GstBuffer *)(buf)) #define GST_BUFFER_FLAGS(buf) \ (GST_BUFFER(buf)->flags) diff --git a/gst/gstclock.c b/gst/gstclock.c index 41300d321d..8f453cce29 100644 --- a/gst/gstclock.c +++ b/gst/gstclock.c @@ -22,7 +22,6 @@ #include static GstClock *the_system_clock = NULL; -static int num; /** * gst_clock_new: @@ -39,8 +38,9 @@ GstClock *gst_clock_new(gchar *name) { clock->sinkmutex = g_mutex_new(); clock->lock = g_mutex_new(); g_mutex_lock(clock->sinkmutex); - num =0; - clock->locking = TRUE; + clock->num = 0; + clock->num_locked = 0; + clock->locking = FALSE; return clock; } @@ -56,87 +56,83 @@ void gst_clock_register(GstClock *clock, GstObject *obj) { if (GST_IS_SINK(obj)) { DEBUG("gst_clock: setting registered sink object 0x%p\n", obj); clock->sinkobjects = g_list_append(clock->sinkobjects, obj); - num++; + clock->num++; } } void gst_clock_set(GstClock *clock, GstClockTime time) { struct timeval tfnow; - GstClockTime target, now; + GstClockTime now; - g_mutex_lock(clock->lock); gettimeofday(&tfnow, (struct timezone *)NULL); - now = tfnow.tv_sec*1000000+tfnow.tv_usec; - clock->adjust = now - (clock->start_time + time); - clock->current_time = (clock->start_time + time); - //DEBUG("gst_clock: setting clock to %llu %llu %lld\n", (guint64)clock->start_time+time, (guint64)now, (gint64)clock->adjust); + now = tfnow.tv_sec*1000000LL+tfnow.tv_usec; + g_mutex_lock(clock->lock); + clock->start_time = now - time; g_mutex_unlock(clock->lock); + DEBUG("gst_clock: setting clock to %llu %llu %llu\n", time, now, clock->start_time); +} + +GstClockTimeDiff gst_clock_current_diff(GstClock *clock, GstClockTime time) +{ + struct timeval tfnow; + GstClockTime now; + + gettimeofday(&tfnow, (struct timezone *)NULL); + g_mutex_lock(clock->lock); + now = ((guint64)tfnow.tv_sec*1000000LL+tfnow.tv_usec) - (guint64)clock->start_time; + //if (clock->locking) now = 0; + g_mutex_unlock(clock->lock); + + //DEBUG("gst_clock: diff with for time %08llu %08lld %08lld %08llu\n", time, now, GST_CLOCK_DIFF(time, now), clock->start_time); + + return GST_CLOCK_DIFF(time, now); } void gst_clock_reset(GstClock *clock) { struct timeval tfnow; gettimeofday(&tfnow, (struct timezone *)NULL); - clock->start_time = tfnow.tv_sec*1000000+tfnow.tv_usec; + g_mutex_lock(clock->lock); + clock->start_time = ((guint64)tfnow.tv_sec)*1000000LL+tfnow.tv_usec; clock->current_time = clock->start_time; clock->adjust = 0LL; DEBUG("gst_clock: setting start clock %llu\n", clock->start_time); + //clock->locking = TRUE; + g_mutex_unlock(clock->lock); } void gst_clock_wait(GstClock *clock, GstClockTime time, GstObject *obj) { struct timeval tfnow; - GstClockTime target, now; + GstClockTime now; GstClockTimeDiff diff; GList *elements; - DEBUG("gst_clock: requesting clock object 0x%p\n", obj); - g_mutex_lock(clock->lock); - elements = clock->sinkobjects; - while (elements && clock->locking) { - if (elements->data == obj) { - DEBUG("gst_clock: registered sink object 0x%p\n", obj); - num--; - if (num) { - DEBUG("gst_clock: 0x%p locked\n", obj); - g_mutex_unlock(clock->lock); - g_mutex_lock(clock->sinkmutex); - g_mutex_lock(clock->lock); - clock->locking = FALSE; - } - else { - gst_clock_reset(clock); - DEBUG("gst_clock: unlock all %p\n", obj); - g_mutex_unlock(clock->sinkmutex); - clock->locking = FALSE; - } - break; - } - elements = g_list_next(elements); - } + //DEBUG("gst_clock: requesting clock object 0x%p %08llu %08llu\n", obj, time, clock->current_time); - target = clock->start_time + time; gettimeofday(&tfnow, (struct timezone *)NULL); - now = tfnow.tv_sec*1000000+tfnow.tv_usec + clock->adjust; + g_mutex_lock(clock->lock); + now = tfnow.tv_sec*1000000LL+tfnow.tv_usec - clock->start_time; //now = clock->current_time + clock->adjust; - //DEBUG("gst_clock: 0x%p waiting for time %llu %llu\n", obj, time, target); - - diff = GST_CLOCK_DIFF(target, now); + diff = GST_CLOCK_DIFF(time, now); // if we are not behind wait a bit + DEBUG("gst_clock: %s waiting for time %08llu %08llu %08lld\n", gst_element_get_name(GST_ELEMENT(obj)), time, now, diff); - if (diff > 1000 ) { - tfnow.tv_usec = diff % 1000000; + g_mutex_unlock(clock->lock); + if (diff > 10000 ) { + tfnow.tv_usec = (diff % 1000000); tfnow.tv_sec = diff / 1000000; // FIXME, this piece of code does not work with egcs optimisations on, had to use the following line - if (tfnow.tv_sec) fprintf(stderr, "gst_clock: waiting %u %llu %llu %llu seconds\n", (int)tfnow.tv_sec, now, diff, target); - g_mutex_unlock(clock->lock); + if (!tfnow.tv_sec) { + select(0, NULL, NULL, NULL, &tfnow); + } + else fprintf(stderr, "gst_clock: waiting %u %llu %llu %llu seconds\n", (int)tfnow.tv_sec, now, diff, time); //DEBUG("gst_clock: 0x%p waiting for time %llu %llu %lld %llu\n", obj, time, target, diff, now); - select(0, NULL, NULL, NULL, &tfnow); - //DEBUG("gst_clock: 0x%p waiting done time %llu %llu\n", obj, time, target); - g_mutex_lock(clock->lock); + //DEBUG("waiting %d.%08d\n",tfnow.tv_sec, tfnow.tv_usec); + //DEBUG("gst_clock: 0x%p waiting done time %llu \n", obj, time); } + DEBUG("gst_clock: %s waiting for time %08llu %08llu %08lld done \n", gst_element_get_name(GST_ELEMENT(obj)), time, now, diff); // clock->current_time = clock->start_time + time; - g_mutex_unlock(clock->lock); //gst_clock_set(clock, time); } diff --git a/gst/gstclock.h b/gst/gstclock.h index d11e71882f..ba6452bc18 100644 --- a/gst/gstclock.h +++ b/gst/gstclock.h @@ -43,6 +43,7 @@ struct _GstClock { GstClockTimeDiff adjust; gboolean locking; GList *sinkobjects; + gint num, num_locked; GMutex *sinkmutex; GMutex *lock; }; @@ -54,6 +55,7 @@ void gst_clock_register(GstClock *clock, GstObject *obj); void gst_clock_set(GstClock *clock, GstClockTime time); void gst_clock_reset(GstClock *clock); void gst_clock_wait(GstClock *clock, GstClockTime time, GstObject *obj); +GstClockTimeDiff gst_clock_current_diff(GstClock *clock, GstClockTime time); #ifdef __cplusplus } diff --git a/gst/gstelement.c b/gst/gstelement.c index ea6a45c271..82e8a9e7f8 100644 --- a/gst/gstelement.c +++ b/gst/gstelement.c @@ -39,7 +39,6 @@ static void gst_element_class_init(GstElementClass *klass); static void gst_element_init(GstElement *element); static void gst_element_real_destroy(GtkObject *object); - static GstObjectClass *parent_class = NULL; static guint gst_element_signals[LAST_SIGNAL] = { 0 }; @@ -265,7 +264,7 @@ void gst_element_connect(GstElement *src,gchar *srcpadname, * condition. It results in the "error" signal. */ void gst_element_error(GstElement *element,gchar *error) { - g_error("error in element '%s': %s\n",element->name,error); + g_error("GstElement: error in element '%s': %s\n",element->name,error); gtk_signal_emit(GTK_OBJECT(element),gst_element_signals[ERROR],error); } @@ -331,7 +330,7 @@ gboolean gst_element_change_state(GstElement *element, // element->name,state); /* deal with the inverted state */ -// g_print("changing element state, was %08lx",GST_STATE(element)); + //g_print("changing element state, was %08lx\n",GST_STATE(element)); if (state & GST_STATE_MAX) GST_STATE_UNSET(element,~state); else @@ -497,7 +496,7 @@ xmlNodePtr gst_element_save_thyself(GstElement *element,xmlNodePtr parent) { break; case GTK_TYPE_DOUBLE: xmlNewChild(arg,NULL,"value", - g_strdup_printf("%lf",GTK_VALUE_DOUBLE(args[i]))); + g_strdup_printf("%g",GTK_VALUE_DOUBLE(args[i]))); break; case GTK_TYPE_STRING: xmlNewChild(arg,NULL,"value",GTK_VALUE_STRING(args[i])); diff --git a/gst/gstelement.h b/gst/gstelement.h index 4a3387004d..966c33e3e0 100644 --- a/gst/gstelement.h +++ b/gst/gstelement.h @@ -52,11 +52,11 @@ typedef enum { #define GST_STATE_IS_SET(obj,flag) (GST_STATE (obj) & (flag)) #define GST_STATE_SET(obj,flag) \ G_STMT_START{ (GST_STATE (obj) |= (flag)); \ -gst_info("set '%s' state %d\n",gst_element_get_name(obj),flag); \ +gst_info("GstElement: set '%s' state %d\n",gst_element_get_name(obj),flag); \ }G_STMT_END #define GST_STATE_UNSET(obj,flag) \ G_STMT_START{ (GST_STATE (obj) &= ~(flag)); \ -gst_info("unset '%s' state %d\n",gst_element_get_name(obj),flag); \ +gst_info("GstElement: unset '%s' state %d\n",gst_element_get_name(obj),flag); \ }G_STMT_END diff --git a/gst/gstpad.c b/gst/gstpad.c index 27daada54f..29f577a97d 100644 --- a/gst/gstpad.c +++ b/gst/gstpad.c @@ -18,6 +18,8 @@ */ +//#define DEBUG_ENABLED +#include #include #include #include @@ -80,6 +82,7 @@ static void gst_pad_init(GstPad *pad) { pad->peer = NULL; pad->chain = NULL; pad->pull = NULL; + pad->qos = NULL; pad->parent = NULL; pad->ghostparents = NULL; } @@ -166,6 +169,13 @@ void gst_pad_set_chain_function(GstPad *pad,GstPadChainFunction chain) { pad->chain = chain; } +void gst_pad_set_qos_function(GstPad *pad,GstPadQoSFunction qos) { + g_return_if_fail(pad != NULL); + g_return_if_fail(GST_IS_PAD(pad)); + + pad->qos = qos; +} + void gst_pad_push(GstPad *pad,GstBuffer *buffer) { g_return_if_fail(pad != NULL); g_return_if_fail(GST_IS_PAD(pad)); @@ -179,9 +189,9 @@ void gst_pad_push(GstPad *pad,GstBuffer *buffer) { // else we're likely going to have to coroutine it else { pad->peer->bufpen = buffer; - g_print("would switch to a coroutine here...\n"); + g_print("GstPad: would switch to a coroutine here...\n"); if (!GST_IS_ELEMENT(pad->peer->parent)) - g_print("eek, this isn't an element!\n"); + g_print("GstPad: eek, this isn't an element!\n"); if (GST_ELEMENT(pad->peer->parent)->threadstate != NULL) cothread_switch(GST_ELEMENT(pad->peer->parent)->threadstate); } @@ -195,12 +205,12 @@ GstBuffer *gst_pad_pull(GstPad *pad) { g_return_val_if_fail(pad != NULL, NULL); g_return_val_if_fail(GST_IS_PAD(pad), NULL); - // if the pull function exists for the pad, call it directly - if (pad->pull) { - return (pad->pull)(pad->peer); + // if the pull function exists for the pad, call it directly + if (pad->pull) { + return (pad->pull)(pad->peer); // else we're likely going to have to coroutine it - } else if (pad->bufpen == NULL) { - g_print("no buffer available, will have to do something about it\n"); + } else if (pad->bufpen == NULL) { + g_print("GstPad: no buffer available, will have to do something about it\n"); peerparent = GST_ELEMENT(pad->peer->parent); // if they're a cothread too, we can just switch to them if (peerparent->threadstate != NULL) { @@ -208,11 +218,11 @@ GstBuffer *gst_pad_pull(GstPad *pad) { // otherwise we have to switch to the main thread } else { state = cothread_main(GST_ELEMENT(pad->parent)->threadstate->ctx); - g_print("switching to supposed 0th thread at %p\n",state); + g_print("GstPad: switching to supposed 0th thread at %p\n",state); cothread_switch(state); } } else { - g_print("buffer available, pulling\n"); + g_print("GstPad: buffer available, pulling\n"); buf = pad->bufpen; pad->bufpen = NULL; return buf; @@ -230,6 +240,41 @@ void gst_pad_chain(GstPad *pad) { (pad->chain)(pad,pad->bufpen); } +/** + * gst_pad_handle_qos: + * @element: element to change state of + * @state: new element state + * + */ +void gst_pad_handle_qos(GstPad *pad, + glong qos_message) +{ + GstElement *element; + GList *pads; + GstPad *target_pad; + + DEBUG("gst_pad_handle_qos(\"%s\",%08ld)\n", GST_ELEMENT(pad->parent)->name,qos_message); + + if (pad->qos) { + (pad->qos)(pad,qos_message); + } + else { + element = GST_ELEMENT(pad->peer->parent); + + pads = element->pads; + DEBUG("gst_pad_handle_qos recurse(\"%s\",%08ld)\n", element->name,qos_message); + while (pads) { + target_pad = GST_PAD(pads->data); + if (target_pad->direction == GST_PAD_SINK) { + gst_pad_handle_qos(target_pad, qos_message); + } + pads = g_list_next(pads); + } + } + + return; +} + void gst_pad_disconnect(GstPad *srcpad,GstPad *sinkpad) { /* generic checks */ diff --git a/gst/gstpad.h b/gst/gstpad.h index d48b34a1e9..10879c1190 100644 --- a/gst/gstpad.h +++ b/gst/gstpad.h @@ -53,6 +53,7 @@ typedef struct _GstPadClass GstPadClass; typedef void (*GstPadChainFunction) (GstPad *pad,GstBuffer *buf); typedef GstBuffer *(*GstPadPullFunction) (GstPad *pad); typedef void (*GstPadPushFunction) (GstPad *pad); +typedef void (*GstPadQoSFunction) (GstPad *pad, glong qos_message); typedef enum { GST_PAD_UNKNOWN, @@ -77,6 +78,7 @@ struct _GstPad { GstPadChainFunction chain; GstPadPullFunction pull; + GstPadQoSFunction qos; GstObject *parent; GList *ghostparents; @@ -93,6 +95,7 @@ void gst_pad_destroy(GstPad *pad); GstPadDirection gst_pad_get_direction(GstPad *pad); void gst_pad_set_chain_function(GstPad *pad,GstPadChainFunction chain); void gst_pad_set_pull_function(GstPad *pad, GstPadPullFunction pull); +void gst_pad_set_qos_function(GstPad *pad, GstPadQoSFunction qos); guint16 gst_pad_get_type_id(GstPad *pad); void gst_pad_set_type_id(GstPad *pad,guint16 id); @@ -114,6 +117,7 @@ void gst_pad_disconnect(GstPad *srcpad,GstPad *sinkpad); void gst_pad_push(GstPad *pad,GstBuffer *buffer); GstBuffer *gst_pad_pull(GstPad *pad); +void gst_pad_handle_qos(GstPad *pad, glong qos_message); xmlNodePtr gst_pad_save_thyself(GstPad *pad,xmlNodePtr parent); diff --git a/gst/gstpipeline.c b/gst/gstpipeline.c index b62f391b1f..edcbea9a5b 100644 --- a/gst/gstpipeline.c +++ b/gst/gstpipeline.c @@ -105,7 +105,7 @@ GstPipeline *gst_pipeline_new(guchar *name) { } static void gst_pipeline_prepare(GstPipeline *pipeline) { - g_print("preparing pipeline for playing\n"); + g_print("GstPipeline: preparing pipeline \"%s\" for playing\n", gst_element_get_name(GST_ELEMENT(pipeline))); } @@ -119,13 +119,14 @@ static gboolean gst_pipeline_change_state(GstElement *element, switch (state) { case GST_STATE_RUNNING: /* we need to set up internal state */ - g_print("preparing pipeline \"%s\" for iterations:\n", + g_print("GstPipeline: preparing pipeline \"%s\" for iterations:\n", gst_element_get_name(GST_ELEMENT(element))); gst_pipeline_prepare(pipeline); break; case ~GST_STATE_RUNNING: /* tear down the internal state */ - g_print("tearing down pipelines's iteration state\n"); + g_print("GstPipeline: tearing down pipelines's \"%s\" iteration state\n", + gst_element_get_name(GST_ELEMENT(element))); break; default: break; diff --git a/gst/gstthread.c b/gst/gstthread.c index c88f7e0b65..cc19d4bfe9 100644 --- a/gst/gstthread.c +++ b/gst/gstthread.c @@ -17,6 +17,7 @@ * Boston, MA 02111-1307, USA. */ +#include #include GstElementDetails gst_thread_details = { @@ -117,13 +118,13 @@ static void gst_thread_set_arg(GtkObject *object,GtkArg *arg,guint id) { switch(id) { case ARG_CREATE_THREAD: if (GTK_VALUE_BOOL(*arg)) { - gst_info("turning ON the creation of the thread\n"); + gst_info("gstthread: turning ON the creation of the thread\n"); GST_FLAG_SET(object,GST_THREAD_CREATE); - gst_info("flags are 0x%08x\n",GST_FLAGS(object)); + gst_info("gstthread: flags are 0x%08x\n",GST_FLAGS(object)); } else { - gst_info("turning OFF the creation of the thread\n"); + gst_info("gstthread: turning OFF the creation of the thread\n"); GST_FLAG_UNSET(object,GST_THREAD_CREATE); - gst_info("flags are 0x%08x\n",GST_FLAGS(object)); + gst_info("gstthread: flags are 0x%08x\n",GST_FLAGS(object)); } break; default: @@ -175,7 +176,7 @@ static void gst_thread_prepare(GstThread *thread) { while (elements) { element = GST_ELEMENT(elements->data); if (GST_IS_SRC(element)) { - gst_info("element \"%s\" is a source entry point for the thread\n", + gst_info("gstthread: element \"%s\" is a source entry point for the thread\n", gst_element_get_name(GST_ELEMENT(element))); thread->entries = g_list_prepend(thread->entries,element); thread->numentries++; @@ -195,7 +196,7 @@ static void gst_thread_prepare(GstThread *thread) { /* if it's a connection and it's not ours... */ if (GST_IS_CONNECTION(outside) && (gst_object_get_parent(GST_OBJECT(outside)) != GST_OBJECT(thread))) { - gst_info("element \"%s\" is the external source Connection \ + gst_info("gstthread: element \"%s\" is the external source Connection \ for internal element \"%s\"\n", gst_element_get_name(GST_ELEMENT(outside)), gst_element_get_name(GST_ELEMENT(element))); @@ -208,7 +209,7 @@ for internal element \"%s\"\n", } elements = g_list_next(elements); } - gst_info("have %d entries into thread\n",thread->numentries); + gst_info("gstthread: have %d entries into thread\n",thread->numentries); } @@ -227,7 +228,7 @@ static gboolean gst_thread_change_state(GstElement *element, case GST_STATE_RUNNING: if (!stateset) return FALSE; /* we want to prepare our internal state for doing the iterations */ - gst_info("preparing thread \"%s\" for iterations:\n", + gst_info("gstthread: preparing thread \"%s\" for iterations:\n", gst_element_get_name(GST_ELEMENT(element))); gst_thread_prepare(thread); if (thread->numentries == 0) @@ -235,14 +236,14 @@ static gboolean gst_thread_change_state(GstElement *element, /* set the state to idle */ GST_FLAG_UNSET(thread,GST_THREAD_STATE_SPINNING); /* create the thread if that's what we're supposed to do */ - gst_info("flags are 0x%08x\n",GST_FLAGS(thread)); + gst_info("gstthread: flags are 0x%08x\n",GST_FLAGS(thread)); if (GST_FLAG_IS_SET(thread,GST_THREAD_CREATE)) { - gst_info("starting thread \"%s\"\n", + gst_info("gstthread: starting thread \"%s\"\n", gst_element_get_name(GST_ELEMENT(element))); pthread_create(&thread->thread_id,NULL, gst_thread_main_loop,thread); } else { - gst_info("NOT starting thread \"%s\"\n", + gst_info("gstthread: NOT starting thread \"%s\"\n", gst_element_get_name(GST_ELEMENT(element))); } return TRUE; @@ -254,19 +255,19 @@ static gboolean gst_thread_change_state(GstElement *element, gst_thread_signal_thread(thread); pthread_join(thread->thread_id,0); /* tear down the internal state */ - gst_info("tearing down thread's iteration state\n"); + gst_info("gstthread: tearing down thread's iteration state\n"); /* FIXME do stuff */ break; case GST_STATE_PLAYING: if (!stateset) return FALSE; - gst_info("starting thread \"%s\"\n", + gst_info("gstthread: starting thread \"%s\"\n", gst_element_get_name(GST_ELEMENT(element))); GST_FLAG_SET(thread,GST_THREAD_STATE_SPINNING); gst_thread_signal_thread(thread); return TRUE; break; case ~GST_STATE_PLAYING: - gst_info("stopping thread \"%s\"\n", + gst_info("gstthread: stopping thread \"%s\"\n", gst_element_get_name(GST_ELEMENT(element))); GST_FLAG_UNSET(thread,GST_THREAD_STATE_SPINNING); gst_thread_signal_thread(thread); @@ -288,7 +289,8 @@ static gboolean gst_thread_change_state(GstElement *element, void *gst_thread_main_loop(void *arg) { GstThread *thread = GST_THREAD(arg); - gst_info("HI, IN MAIN THREAD LOOP!\n"); + gst_info("gstthread: thread \"%s\" is running with PID %d\n", + gst_element_get_name(GST_ELEMENT(thread)), getpid()); while(!GST_FLAG_IS_SET(thread,GST_THREAD_STATE_REAPING)) { if (GST_FLAG_IS_SET(thread,GST_THREAD_STATE_SPINNING)) @@ -302,7 +304,8 @@ void *gst_thread_main_loop(void *arg) { GST_FLAG_UNSET(thread,GST_THREAD_STATE_REAPING); - gst_info("GOODBYE, LEAVING MAIN THREAD LOOP!\n"); + gst_info("gstthread: thread \"%s\" is stopped\n", + gst_element_get_name(GST_ELEMENT(thread))); return NULL; } @@ -323,6 +326,8 @@ void gst_thread_iterate(GstThread *thread) { entries = thread->entries; + DEBUG("gstthread: %s: thread iterate\n", gst_element_get_name(GST_ELEMENT(thread))); + while (entries) { entry = GST_ELEMENT(entries->data); if (GST_IS_SRC(entry)) @@ -333,6 +338,7 @@ void gst_thread_iterate(GstThread *thread) { g_assert_not_reached(); entries = g_list_next(entries); } + DEBUG("gstthread: %s: thread iterate done\n", gst_element_get_name(GST_ELEMENT(thread))); //g_print(","); } diff --git a/gstplay/callbacks.c b/gstplay/callbacks.c index ad4f30232d..2df293b5fc 100644 --- a/gstplay/callbacks.c +++ b/gstplay/callbacks.c @@ -52,6 +52,8 @@ void on_play2_activate (GtkMenuItem *menuitem, gpointer user_data) { + update_buttons(0); + change_state(GSTPLAY_PLAYING); } @@ -60,6 +62,8 @@ void on_pause1_activate (GtkMenuItem *menuitem, gpointer user_data) { + update_buttons(1); + change_state(GSTPLAY_PAUSE); } @@ -68,6 +72,8 @@ void on_stop1_activate (GtkMenuItem *menuitem, gpointer user_data) { + update_buttons(2); + change_state(GSTPLAY_STOPPED); } diff --git a/gstplay/gstplay.c b/gstplay/gstplay.c index a1b6fe0e84..6ef0fd2e95 100644 --- a/gstplay/gstplay.c +++ b/gstplay/gstplay.c @@ -7,6 +7,10 @@ # include #endif +//#define DEBUG_ENABLED + +#include + #include "gstplay.h" #include "interface.h" @@ -14,9 +18,15 @@ #include "codecs.h" +#define MUTEX_STATUS() (g_mutex_trylock(gdk_threads_mutex)? g_mutex_unlock(gdk_threads_mutex), "was not locked" : "was locked") + + +#define BUFFER 4 + extern gboolean _gst_plugin_spew; gboolean idle_func(gpointer data); -GstElement *show, *audio_play; +GstElement *show, *video_render_queue; +GstElement *audio_play, *audio_render_queue; GstElement *src; GstPipeline *pipeline; GstElement *parse = NULL; @@ -31,6 +41,8 @@ static void frame_displayed(GstSrc *asrc) guint mux_rate; static int prev_time = -1; + DEBUG("gstplay: frame displayed %s\n", MUTEX_STATUS()); + mux_rate = gst_util_get_int_arg(GTK_OBJECT(parse),"mux_rate"); size = gst_util_get_int_arg(GTK_OBJECT(src),"size"); time = (size*8)/mux_rate; @@ -53,10 +65,13 @@ static void frame_displayed(GstSrc *asrc) picture_shown = TRUE; prev_time = frame_time; + DEBUG("gstplay: frame displayed end %s\n", MUTEX_STATUS()); } gboolean idle_func(gpointer data) { + DEBUG("idle start %s\n",MUTEX_STATUS()); gst_src_push(GST_SRC(data)); + DEBUG("idle stop %s\n",MUTEX_STATUS()); return TRUE; } @@ -67,16 +82,31 @@ static void eof(GstSrc *src) { void show_next_picture() { picture_shown = FALSE; + DEBUG("gstplay: next picture %s\n", MUTEX_STATUS()); while (!picture_shown) { + gdk_threads_leave(); gst_src_push(GST_SRC(src)); - gtk_main_iteration_do(FALSE); + gdk_threads_enter(); } + DEBUG("gstplay: next found %s\n", MUTEX_STATUS()); } void mute_audio(gboolean mute) { gtk_object_set(GTK_OBJECT(audio_play),"mute",mute,NULL); } +void gstplay_parse_state_changed(GstElement *element, gint state, gpointer data) +{ + printf("gstplay: element \"%s\" state changed %d\n", gst_element_get_name(element), state); + + if (state == GST_STATE_COMPLETE) { + g_print("gstplay: setting to PLAYING state\n"); + gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING); + gst_clock_reset(gst_clock_get_system()); + g_print("gstplay: PLAYING\n"); + } +} + void change_state(GstPlayState new_state) { if (new_state == state) return; @@ -126,12 +156,12 @@ static void have_type(GstSink *sink) { gst_element_get_pad(GST_ELEMENT(sink),"sink")); if (strstr(gsttype->mime, "mpeg1-system")) { - parse = gst_elementfactory_make("mpeg1parse","parse"); + parse = gst_elementfactory_make("mpeg1parse","mpeg1_system_parse"); gtk_signal_connect(GTK_OBJECT(parse),"new_pad", GTK_SIGNAL_FUNC(mpeg1_new_pad_created),pipeline); } else if (strstr(gsttype->mime, "mpeg2-system")) { - parse = gst_elementfactory_make("mpeg2parse","parse"); + parse = gst_elementfactory_make("mpeg2parse","mpeg2_system_parse"); gtk_signal_connect(GTK_OBJECT(parse),"new_pad", GTK_SIGNAL_FUNC(mpeg2_new_pad_created),pipeline); } @@ -152,6 +182,8 @@ static void have_type(GstSink *sink) { gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(parse)); gst_pad_connect(gst_element_get_pad(src,"src"), gst_element_get_pad(parse,"sink")); + gtk_signal_connect(GTK_OBJECT(parse),"state_change", + GTK_SIGNAL_FUNC(gstplay_parse_state_changed),pipeline); } gtk_object_set(GTK_OBJECT(src),"offset",0,NULL); @@ -162,10 +194,13 @@ main (int argc, char *argv[]) { GtkWidget *window1; GstElement *typefind; + GstElement *video_render_thread; + GstElement *audio_render_thread; bindtextdomain (PACKAGE, PACKAGE_LOCALE_DIR); textdomain (PACKAGE); + g_thread_init(NULL); gtk_init(&argc,&argv); gnome_init ("gstreamer", VERSION, argc, argv); gst_init(&argc,&argv); @@ -173,28 +208,57 @@ main (int argc, char *argv[]) gst_plugin_load("mpeg2parse"); gst_plugin_load("mp1videoparse"); gst_plugin_load("mp3parse"); - //gst_plugin_load("parsewav"); - //gst_plugin_load("parseavi"); + gst_plugin_load("parsewav"); + gst_plugin_load("parseavi"); gst_plugin_load("videosink"); g_snprintf(statusline, 200, "seeking"); + pipeline = gst_pipeline_new("main_pipeline"); + g_return_val_if_fail(pipeline != NULL, -1); + + video_render_thread = gst_thread_new("video_render_thread"); + g_return_val_if_fail(video_render_thread != NULL, -1); show = gst_elementfactory_make("videosink","show"); g_return_val_if_fail(show != NULL, -1); + gtk_object_set(GTK_OBJECT(show),"xv_enabled",FALSE,NULL); window1 = create_window1 (gst_util_get_widget_arg(GTK_OBJECT(show),"widget")); gtk_widget_show (window1); gtk_signal_connect(GTK_OBJECT(show),"frame_displayed", GTK_SIGNAL_FUNC(frame_displayed),NULL); + gst_bin_add(GST_BIN(video_render_thread),GST_ELEMENT(show)); + gst_element_add_ghost_pad(GST_ELEMENT(video_render_thread), + gst_element_get_pad(show,"sink")); + video_render_queue = gst_elementfactory_make("queue","video_render_queue"); + gtk_object_set(GTK_OBJECT(video_render_queue),"max_level",BUFFER,NULL); + gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(video_render_queue)); + gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(video_render_thread)); + gst_pad_connect(gst_element_get_pad(video_render_queue,"src"), + gst_element_get_pad(video_render_thread,"sink")); + gtk_object_set(GTK_OBJECT(video_render_thread),"create_thread",TRUE,NULL); + + + audio_render_thread = gst_thread_new("audio_render_thread"); + g_return_val_if_fail(audio_render_thread != NULL, -1); audio_play = gst_elementfactory_make("audiosink","play_audio"); + gst_bin_add(GST_BIN(audio_render_thread),GST_ELEMENT(audio_play)); + gst_element_add_ghost_pad(GST_ELEMENT(audio_render_thread), + gst_element_get_pad(audio_play,"sink")); - pipeline = gst_pipeline_new("pipeline"); - g_return_val_if_fail(pipeline != NULL, -1); + audio_render_queue = gst_elementfactory_make("queue","audio_render_queue"); + gtk_object_set(GTK_OBJECT(audio_render_queue),"max_level",BUFFER,NULL); + gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(audio_render_queue)); + gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(audio_render_thread)); + gst_pad_connect(gst_element_get_pad(audio_render_queue,"src"), + gst_element_get_pad(audio_render_thread,"sink")); + gtk_object_set(GTK_OBJECT(audio_render_thread),"create_thread",TRUE,NULL); - src = gst_elementfactory_make("disksrc","src"); + + src = gst_elementfactory_make("disksrc","disk_src"); g_return_val_if_fail(src != NULL, -1); - gtk_object_set(GTK_OBJECT(src),"location",argv[1],NULL); g_print("should be using file '%s'\n",argv[1]); + gtk_object_set(GTK_OBJECT(src),"location",argv[1],NULL); gtk_window_set_title (GTK_WINDOW (window1), g_strdup_printf("GStreamer Media player - %s", argv[1])); @@ -215,6 +279,8 @@ main (int argc, char *argv[]) gst_pad_connect(gst_element_get_pad(src,"src"), gst_element_get_pad(typefind,"sink")); + g_print("setting to PREROLL state\n"); + gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PREROLL); g_print("setting to RUNNING state\n"); gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_RUNNING); @@ -222,7 +288,9 @@ main (int argc, char *argv[]) change_state(GSTPLAY_PLAYING); + gdk_threads_enter(); gtk_main(); + gdk_threads_leave(); return 0; } diff --git a/gstplay/mpeg1.c b/gstplay/mpeg1.c index f1d12eedfd..cb6db97283 100644 --- a/gstplay/mpeg1.c +++ b/gstplay/mpeg1.c @@ -1,5 +1,5 @@ -#define BUFFER 15 +#define BUFFER 20 #define VIDEO_DECODER "mpeg_play" #ifdef HAVE_CONFIG_H @@ -13,8 +13,8 @@ extern gboolean _gst_plugin_spew; -extern GstElement *show; -extern GstElement *audio_play; +extern GstElement *video_render_queue; +extern GstElement *audio_render_queue; void mpeg1_new_pad_created(GstElement *parse,GstPad *pad,GstElement *pipeline) { @@ -26,7 +26,7 @@ void mpeg1_new_pad_created(GstElement *parse,GstPad *pad,GstElement *pipeline) // connect to audio pad //if (0) { - if (strncmp(gst_pad_get_name(pad), "audio_", 6) == 0 && audio_play) { + if (strncmp(gst_pad_get_name(pad), "audio_", 6) == 0 && audio_render_queue) { gst_plugin_load("mp3parse"); gst_plugin_load("mpg123"); // construct internal pipeline elements @@ -40,7 +40,6 @@ void mpeg1_new_pad_created(GstElement *parse,GstPad *pad,GstElement *pipeline) g_return_if_fail(audio_thread != NULL); gst_bin_add(GST_BIN(audio_thread),GST_ELEMENT(parse_audio)); gst_bin_add(GST_BIN(audio_thread),GST_ELEMENT(decode)); - gst_bin_add(GST_BIN(audio_thread),GST_ELEMENT(audio_play)); // set up pad connections gst_element_add_ghost_pad(GST_ELEMENT(audio_thread), @@ -48,7 +47,7 @@ void mpeg1_new_pad_created(GstElement *parse,GstPad *pad,GstElement *pipeline) gst_pad_connect(gst_element_get_pad(parse_audio,"src"), gst_element_get_pad(decode,"sink")); gst_pad_connect(gst_element_get_pad(decode,"src"), - gst_element_get_pad(audio_play,"sink")); + gst_element_get_pad(audio_render_queue,"sink")); // construct queue and connect everything in the main pipelie audio_queue = gst_elementfactory_make("queue","audio_queue"); @@ -64,15 +63,15 @@ void mpeg1_new_pad_created(GstElement *parse,GstPad *pad,GstElement *pipeline) gtk_object_set(GTK_OBJECT(audio_thread),"create_thread",TRUE,NULL); g_print("setting to RUNNING state\n"); gst_element_set_state(GST_ELEMENT(audio_thread),GST_STATE_RUNNING); - g_print("setting to PLAYING state\n"); - gst_element_set_state(GST_ELEMENT(audio_thread),GST_STATE_PLAYING); + //g_print("setting to PLAYING state\n"); + //gst_element_set_state(GST_ELEMENT(audio_thread),GST_STATE_PLAYING); } else if (strncmp(gst_pad_get_name(pad), "video_", 6) == 0) { //} else if (0) { - mpeg1_setup_video_thread(pad, show, pipeline); + mpeg1_setup_video_thread(pad, video_render_queue, pipeline); } } -void mpeg1_setup_video_thread(GstPad *pad, GstElement *show, GstElement *pipeline) +void mpeg1_setup_video_thread(GstPad *pad, GstElement *video_render_queue, GstElement *pipeline) { GstElement *parse_video, *decode_video; GstElement *video_queue; @@ -91,7 +90,6 @@ void mpeg1_setup_video_thread(GstPad *pad, GstElement *show, GstElement *pipelin g_return_if_fail(video_thread != NULL); gst_bin_add(GST_BIN(video_thread),GST_ELEMENT(parse_video)); gst_bin_add(GST_BIN(video_thread),GST_ELEMENT(decode_video)); - gst_bin_add(GST_BIN(video_thread),GST_ELEMENT(show)); // set up pad connections gst_element_add_ghost_pad(GST_ELEMENT(video_thread), @@ -99,7 +97,7 @@ void mpeg1_setup_video_thread(GstPad *pad, GstElement *show, GstElement *pipelin gst_pad_connect(gst_element_get_pad(parse_video,"src"), gst_element_get_pad(decode_video,"sink")); gst_pad_connect(gst_element_get_pad(decode_video,"src"), - gst_element_get_pad(show,"sink")); + gst_element_get_pad(video_render_queue,"sink")); // construct queue and connect everything in the main pipeline video_queue = gst_elementfactory_make("queue","video_queue"); @@ -115,9 +113,7 @@ void mpeg1_setup_video_thread(GstPad *pad, GstElement *show, GstElement *pipelin gtk_object_set(GTK_OBJECT(video_thread),"create_thread",TRUE,NULL); g_print("setting to RUNNING state\n"); gst_element_set_state(GST_ELEMENT(video_thread),GST_STATE_RUNNING); - g_print("setting to PLAYING state\n"); - gst_element_set_state(GST_ELEMENT(video_thread),GST_STATE_PLAYING); - - g_print("\n"); + //g_print("setting to PLAYING state\n"); + //gst_element_set_state(GST_ELEMENT(video_thread),GST_STATE_PLAYING); } diff --git a/include/mmx.h b/include/mmx.h index 3f899b8d37..9e0ba8b8f9 100644 --- a/include/mmx.h +++ b/include/mmx.h @@ -372,11 +372,11 @@ mmx_ok(void) #define mmx_m2r(op, mem, reg) \ __asm__ __volatile__ (#op " %0, %%" #reg \ : /* nothing */ \ - : "X" (mem)) + : "m" (mem)) #define mmx_r2m(op, reg, mem) \ __asm__ __volatile__ (#op " %%" #reg ", %0" \ - : "=X" (mem) \ + : "=m" (mem) \ : /* nothing */ ) #define mmx_r2r(op, regs, regd) \ @@ -386,8 +386,8 @@ mmx_ok(void) __asm__ __volatile__ ("movq %0, %%mm0\n\t" \ #op " %1, %%mm0\n\t" \ "movq %%mm0, %0" \ - : "=X" (memd) \ - : "X" (mems)) + : "=m" (memd) \ + : "m" (mems)) #endif diff --git a/plugins/elements/gstaudiosink.c b/plugins/elements/gstaudiosink.c index b136419e4f..6c0a24950f 100644 --- a/plugins/elements/gstaudiosink.c +++ b/plugins/elements/gstaudiosink.c @@ -147,9 +147,8 @@ static void gst_audiosink_init(GstAudioSink *audiosink) { audiosink->fd = -1; audiosink->clock = gst_clock_get_system(); gst_clock_register(audiosink->clock, GST_OBJECT(audiosink)); - audiosink->clocktime = 0LL; + //audiosink->clocktime = 0LL; - gst_element_set_state(GST_ELEMENT(audiosink),GST_STATE_COMPLETE); } void gst_audiosink_sync_parms(GstAudioSink *audiosink) { @@ -173,19 +172,20 @@ void gst_audiosink_sync_parms(GstAudioSink *audiosink) { audiosink->frequency,audiosink->format, (audiosink->channels == 2) ? "stereo" : "mono",ospace.bytes, frag); - } GstElement *gst_audiosink_new(gchar *name) { GstElement *audiosink = GST_ELEMENT(gtk_type_new(GST_TYPE_AUDIOSINK)); gst_element_set_name(GST_ELEMENT(audiosink),name); + gst_element_set_state(GST_ELEMENT(audiosink),GST_STATE_COMPLETE); return audiosink; } void gst_audiosink_chain(GstPad *pad,GstBuffer *buf) { GstAudioSink *audiosink; MetaAudioRaw *meta; - count_info info; + gboolean in_flush; + audio_buf_info ospace; g_return_if_fail(pad != NULL); g_return_if_fail(GST_IS_PAD(pad)); @@ -197,6 +197,12 @@ void gst_audiosink_chain(GstPad *pad,GstBuffer *buf) { audiosink = GST_AUDIOSINK(pad->parent); // g_return_if_fail(GST_FLAG_IS_SET(audiosink,GST_STATE_RUNNING)); + if (in_flush = GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLUSH)) { + DEBUG("audiosink: flush\n"); + ioctl(audiosink->fd,SNDCTL_DSP_RESET,0); + } + + meta = (MetaAudioRaw *)gst_buffer_get_first_meta(buf); if (meta != NULL) { if ((meta->format != audiosink->format) || @@ -217,20 +223,20 @@ void gst_audiosink_chain(GstPad *pad,GstBuffer *buf) { gst_trace_add_entry(NULL,0,buf,"audiosink: writing to soundcard"); //g_print("audiosink: writing to soundcard\n"); if (audiosink->fd > 2) { - if (audiosink->clocktime == 0LL) - gst_clock_wait(audiosink->clock, audiosink->clocktime, GST_OBJECT(audiosink)); - ioctl(audiosink->fd,SNDCTL_DSP_GETOPTR,&info); - audiosink->clocktime = (info.bytes*1000000LL)/(audiosink->frequency*audiosink->channels); - //g_print("audiosink: bytes sent %d time %llu\n", info.bytes, audiosink->clocktime); - gst_clock_set(audiosink->clock, audiosink->clocktime); - if (!audiosink->mute) - write(audiosink->fd,GST_BUFFER_DATA(buf),GST_BUFFER_SIZE(buf)); - //audiosink->clocktime += (1000000LL*GST_BUFFER_SIZE(buf)/(audiosink->channels* -// (audiosink->format/8)*(audiosink->frequency))); - //g_print("audiosink: writing to soundcard ok\n"); + if (!audiosink->mute) { + if (gst_clock_current_diff(audiosink->clock, GST_BUFFER_TIMESTAMP(buf)) > 500000) { + } + else { + gst_clock_wait(audiosink->clock, GST_BUFFER_TIMESTAMP(buf), GST_OBJECT(audiosink)); + ioctl(audiosink->fd,SNDCTL_DSP_GETOSPACE,&ospace); + DEBUG("audiosink: (%d bytes buffer)\n", ospace.bytes); + write(audiosink->fd,GST_BUFFER_DATA(buf),GST_BUFFER_SIZE(buf)); + //gst_clock_set(audiosink->clock, GST_BUFFER_TIMESTAMP(buf)); + } + } } } - +end: //g_print("a unref\n"); gst_buffer_unref(buf); //g_print("a done\n"); diff --git a/plugins/elements/gstaudiosink.h b/plugins/elements/gstaudiosink.h index ecd441621c..26a1041032 100644 --- a/plugins/elements/gstaudiosink.h +++ b/plugins/elements/gstaudiosink.h @@ -54,7 +54,7 @@ struct _GstAudioSink { GstPad *sinkpad; - GstClockTime clocktime; + //GstClockTime clocktime; GstClock *clock; /* soundcard state */ int fd; diff --git a/plugins/elements/gstqueue.c b/plugins/elements/gstqueue.c index f383eb2c65..159d88d478 100644 --- a/plugins/elements/gstqueue.c +++ b/plugins/elements/gstqueue.c @@ -20,7 +20,7 @@ //#define DEBUG_ENABLED //#define STATUS_ENABLED #ifdef STATUS_ENABLED -#define STATUS(A) g_print(A) +#define STATUS(A) DEBUG(A, gst_element_get_name(GST_ELEMENT(queue))) #else #define STATUS(A) #endif @@ -144,6 +144,12 @@ static GstBuffer *gst_queue_pull(GstPad *pad) { else return NULL; } +static void gst_queue_cleanup_buffers(gpointer data, gpointer user_data) +{ + DEBUG("queue: %s cleaning buffer %p\n", (gchar *)user_data, data); + gst_buffer_unref(GST_BUFFER(data)); +} + void gst_queue_chain(GstPad *pad,GstBuffer *buf) { GstQueue *queue; gboolean tosignal = FALSE; @@ -157,8 +163,19 @@ void gst_queue_chain(GstPad *pad,GstBuffer *buf) { name = gst_element_get_name(GST_ELEMENT(queue)); /* we have to lock the queue since we span threads */ + + DEBUG("queue: %s adding buffer %p\n", name, buf); GST_LOCK(queue); + + if (GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLUSH)) { + g_list_foreach(queue->queue, gst_queue_cleanup_buffers, name); + g_list_free(queue->queue); + queue->queue = NULL; + queue->level_buffers = 0; + } + + DEBUG("queue: %s: chain %d %p\n", name, queue->level_buffers, buf); if (queue->level_buffers >= queue->max_buffers) { @@ -166,7 +183,7 @@ void gst_queue_chain(GstPad *pad,GstBuffer *buf) { while (queue->level_buffers >= queue->max_buffers) { GST_UNLOCK(queue); g_mutex_lock(queue->fulllock); - STATUS("O"); + STATUS("%s: O\n"); g_cond_wait(queue->fullcond,queue->fulllock); g_mutex_unlock(queue->fulllock); GST_LOCK(queue); @@ -186,7 +203,7 @@ void gst_queue_chain(GstPad *pad,GstBuffer *buf) { // queue->tail = g_list_next(queue->tail); queue->queue = g_list_append(queue->queue,buf); } - STATUS("+"); + STATUS("%s: +\n"); /* if we were empty, but aren't any more, signal a condition */ tosignal = (queue->level_buffers <= 0); @@ -213,19 +230,17 @@ void gst_queue_push(GstConnection *connection) { name = gst_element_get_name(GST_ELEMENT(queue)); + DEBUG("queue: %s push %d\n", name, queue->level_buffers); /* have to lock for thread-safety */ GST_LOCK(queue); - DEBUG("queue: %s push %d\n", name, queue->level_buffers); - if (!queue->level_buffers) { - while (!queue->level_buffers) { - GST_UNLOCK(queue); - g_mutex_lock(queue->emptylock); - STATUS("U"); - g_cond_wait(queue->emptycond,queue->emptylock); - g_mutex_unlock(queue->emptylock); - GST_LOCK(queue); - } + while (!queue->level_buffers) { + GST_UNLOCK(queue); + g_mutex_lock(queue->emptylock); + STATUS("%s: U\n"); + g_cond_wait(queue->emptycond,queue->emptylock); + g_mutex_unlock(queue->emptylock); + GST_LOCK(queue); } front = queue->queue; @@ -233,7 +248,7 @@ void gst_queue_push(GstConnection *connection) { queue->queue = g_list_remove_link(queue->queue,front); g_list_free(front); queue->level_buffers--; - STATUS("-"); + STATUS("%s: -\n"); tosignal = queue->level_buffers < queue->max_buffers; GST_UNLOCK(queue); diff --git a/plugins/elements/gstqueue.h b/plugins/elements/gstqueue.h index 4016b94005..bb9c47bf9e 100644 --- a/plugins/elements/gstqueue.h +++ b/plugins/elements/gstqueue.h @@ -43,7 +43,7 @@ GstElementDetails gst_queue_details; #define GST_IS_QUEUE(obj) \ (GTK_CHECK_TYPE((obj),GST_TYPE_QUEUE)) #define GST_IS_QUEUE_CLASS(obj) \ - (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_QUEUE))) + (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_QUEUE)) typedef struct _GstQueue GstQueue; typedef struct _GstQueueClass GstQueueClass;