sbGStreamerPipeline.cpp
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2010 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
25 #include "sbGStreamerPipeline.h"
26 
27 #include <sbIGStreamerService.h>
28 
29 #include <sbClassInfoUtils.h>
30 #include <nsIInterfaceRequestorUtils.h>
31 #include <nsIRunnable.h>
32 #include <nsServiceManagerUtils.h>
33 #include <nsThreadUtils.h>
34 #include <prlog.h>
35 
36 #include <sbIMediacoreError.h>
37 #include <sbMediacoreError.h>
39 
44 #ifdef PR_LOGGING
45 static PRLogModuleInfo* gGStreamerPipeline = PR_NewLogModule("sbGStreamerPipeline");
46 #define LOG(args) PR_LOG(gGStreamerPipeline, PR_LOG_WARNING, args)
47 #define TRACE(args) PR_LOG(gGStreamerPipeline, PR_LOG_DEBUG, args)
48 #else /* PR_LOGGING */
49 #define LOG(args) /* nothing */
50 #define TRACE(args) /* nothing */
51 #endif /* PR_LOGGING */
52 
55 
59 
60 NS_IMPL_CI_INTERFACE_GETTER1(sbGStreamerPipeline,
61  sbIMediacoreEventTarget)
62 
63 NS_DECL_CLASSINFO(sbGStreamerPipeline)
64 NS_IMPL_THREADSAFE_CI(sbGStreamerPipeline)
65 
66 sbGStreamerPipeline::sbGStreamerPipeline() :
67  mPipeline(NULL),
68  mMonitor(NULL),
69  mPipelineOp(GStreamer::OP_UNKNOWN)
70 {
71  TRACE(("sbGStreamerPipeline[0x%.8x] - Constructed", this));
72 
73  mBaseEventTarget = new sbBaseMediacoreEventTarget(this);
74 }
75 
77 {
78  TRACE(("sbGStreamerPipeline[0x%.8x] - Destructed", this));
79 
81 
82  if (mMonitor) {
83  nsAutoMonitor::DestroyMonitor(mMonitor);
84  }
85 }
86 
87 nsresult
89 {
90  TRACE(("sbGStreamerPipeline[0x%.8x] - Initialise", this));
91 
92  nsresult rv;
93 
94  // We need to make sure the gstreamer service component has been loaded
95  // since it calls gst_init for us.
96  if (!NS_IsMainThread()) {
97  nsCOMPtr<sbIGStreamerService> service =
99  }
100  else {
101  nsCOMPtr<sbIGStreamerService> service =
102  do_GetService(SBGSTREAMERSERVICE_CONTRACTID, &rv);
103  NS_ENSURE_SUCCESS(rv, rv);
104  }
105 
106  mMonitor = nsAutoMonitor::NewMonitor("sbGStreamerPipeline::mMonitor");
107  NS_ENSURE_TRUE(mMonitor, NS_ERROR_OUT_OF_MEMORY);
108 
109  return NS_OK;
110 }
111 
112 nsresult
114 {
115  return NS_ERROR_NOT_IMPLEMENTED;
116 }
117 
118 nsresult
120 {
121  TRACE(("sbGStreamerPipeline[0x%.8x] - SetupPipeline", this));
122  nsresult rv;
123  nsAutoMonitor lock(mMonitor);
124 
125  rv = BuildPipeline();
126  NS_ENSURE_SUCCESS (rv, rv);
127 
128  // BuildPipeline is required to have created the pipeline
129  NS_ENSURE_STATE (mPipeline);
130 
131  GstBus *bus = gst_element_get_bus(mPipeline);
132 
133  // We want to receive state-changed messages when shutting down, so we
134  // need to turn off bus auto-flushing
135  g_object_set(mPipeline, "auto-flush-bus", FALSE, NULL);
136 
137  // Handle GStreamer messages synchronously, either directly or
138  // dispatching to the main thread.
139  gst_bus_set_sync_handler (bus, SyncToAsyncDispatcher,
140  static_cast<sbGStreamerMessageHandler*>(this));
141 
142  gst_object_unref(bus);
143 
144  // Hold a reference to ourselves while we have a pipeline
145  NS_ADDREF_THIS();
146 
147  return NS_OK;
148 }
149 
150 nsresult
152 {
153  TRACE(("sbGStreamerPipeline[0x%.8x] - DestroyPipeline", this));
154 
155  nsresult rv;
156  GstElement *pipeline = NULL;
157 
158  nsAutoMonitor lock(mMonitor);
159  if (mPipeline)
160  pipeline = (GstElement *)gst_object_ref (mPipeline);
161  lock.Exit();
162 
163  if (pipeline) {
164  gst_element_set_state(pipeline, GST_STATE_NULL);
165  gst_object_unref (pipeline);
166  TRACE(("sbGStreamerPipeline[0x%.8x] - DestroyPipeline NULL state.", this));
167  }
168 
169  lock.Enter();
170  if (mPipeline) {
171  // Give subclass a chance to do something after the pipeline has been
172  // stopped, but before we unref it
174  NS_ENSURE_SUCCESS (rv, rv);
175 
176  gst_object_unref(mPipeline);
177  TRACE(("sbGStreamerPipeline[0x%.8x] - DestroyPipeline unref", this));
178  mPipeline = NULL;
179 
180  // Drop our self-reference once we no longer have a pipeline
181  NS_RELEASE_THIS();
182  }
183 
184  return NS_OK;
185 }
186 
188 {
189  // Subclasses will probably override this to handle some messages.
190  return PR_FALSE;
191 }
192 
193 NS_IMETHODIMP
194 sbGStreamerPipeline::PlayPipeline()
195 {
196  TRACE(("sbGStreamerPipeline[0x%.8x] - PlayPipeline", this));
197  GstElement *pipeline = NULL;
198  nsresult rv;
199 
200  nsAutoMonitor lock(mMonitor);
201  if (!mPipeline) {
202  rv = SetupPipeline();
203  NS_ENSURE_SUCCESS (rv, rv);
204  }
205  pipeline = (GstElement *)gst_object_ref (mPipeline);
206  lock.Exit();
207 
208  gst_element_set_state(pipeline, GST_STATE_PLAYING);
209  gst_object_unref (pipeline);
210 
211  return NS_OK;
212 }
213 
214 NS_IMETHODIMP
215 sbGStreamerPipeline::PausePipeline()
216 {
217  TRACE(("sbGStreamerPipeline[0x%.8x] - PausePipeline", this));
218  GstElement *pipeline = NULL;
219  nsresult rv;
220 
221  nsAutoMonitor lock(mMonitor);
222  if (!mPipeline) {
223  rv = SetupPipeline();
224  NS_ENSURE_SUCCESS (rv, rv);
225  }
226  pipeline = (GstElement *)gst_object_ref (mPipeline);
227  lock.Exit();
228 
229  gst_element_set_state(pipeline, GST_STATE_PAUSED);
230  gst_object_unref (pipeline);
231 
232  return NS_OK;
233 }
234 
235 NS_IMETHODIMP
236 sbGStreamerPipeline::StopPipeline()
237 {
238  TRACE(("sbGStreamerPipeline[0x%.8x] - StopPipeline", this));
239  GstElement *pipeline = NULL;
240  nsresult rv;
241  nsAutoMonitor lock(mMonitor);
242 
243  if (mPipeline)
244  pipeline = (GstElement *)gst_object_ref (mPipeline);
245  lock.Exit();
246 
247  if (pipeline) {
248  gst_element_set_state(pipeline, GST_STATE_NULL);
249  gst_object_unref (pipeline);
250 
251  rv = DestroyPipeline();
252  NS_ENSURE_SUCCESS (rv, rv);
253  }
254 
255  return NS_OK;
256 }
257 
259 {
260  nsAutoMonitor mon(mMonitor);
261  mPipelineOp = aPipelineOp;
262  return;
263 }
264 
266 {
267  nsAutoMonitor mon(mMonitor);
268  return mPipelineOp;
269 }
270 
272 {
273  GstMessageType msg_type;
274  msg_type = GST_MESSAGE_TYPE(message);
275 
276  switch (msg_type) {
277  case GST_MESSAGE_ERROR:
278  HandleErrorMessage(message);
279  break;
280  case GST_MESSAGE_WARNING:
281  HandleWarningMessage(message);
282  break;
283  case GST_MESSAGE_STATE_CHANGED:
284  HandleStateChangeMessage(message);
285  break;
286  case GST_MESSAGE_EOS:
287  HandleEOSMessage(message);
288  break;
289  default:
290  LOG(("Got message: %s", gst_message_type_get_name(msg_type)));
291  break;
292  }
293 }
294 
296 {
297  GError *gerror = NULL;
298  gchar *debug = NULL;
299  nsString errormessage;
300  nsCOMPtr<sbIMediacoreError> error;
301  nsresult rv;
302 
303  gst_message_parse_error(message, &gerror, &debug);
304 
305  LOG(("Error message: %s [%s]", GST_STR_NULL (gerror->message),
306  GST_STR_NULL (debug)));
307 
310  op, getter_AddRefs(error));
311  NS_ENSURE_SUCCESS(rv, /* void */);
312 
314 
315  g_error_free (gerror);
316  g_free(debug);
317 
318  rv = StopPipeline();
319  NS_ENSURE_SUCCESS (rv, /* void */);
320 }
321 
323 {
324  GError *error = NULL;
325  gchar *debug = NULL;
326 
327  gst_message_parse_warning(message, &error, &debug);
328 
329  LOG(("Warning message: %s [%s]", GST_STR_NULL (error->message),
330  GST_STR_NULL (debug)));
331 
332  g_warning ("%s [%s]", GST_STR_NULL (error->message), GST_STR_NULL (debug));
333 
334  g_error_free (error);
335  g_free (debug);
336 }
337 
339 {
340  TRACE(("sbGStreamerPipeline[0x%.8x] - HandleEOSMessage", this));
341 
342  nsresult rv = StopPipeline();
343  NS_ENSURE_SUCCESS (rv, /* void */);
344 }
345 
347 {
348 }
349 
351 {
352 }
353 
355 {
356  // Only listen to state-changed messages from top-level pipelines
357  if (GST_IS_PIPELINE (message->src))
358  {
359  GstState oldstate, newstate, pendingstate;
360  gst_message_parse_state_changed (message,
361  &oldstate, &newstate, &pendingstate);
362 
363  gchar *srcname = gst_object_get_name(message->src);
364  LOG(("state-changed: %s changed state from %s to %s", srcname,
365  gst_element_state_get_name (oldstate),
366  gst_element_state_get_name (newstate)));
367  g_free (srcname);
368 
369  if (oldstate == GST_STATE_PAUSED && newstate == GST_STATE_PLAYING)
370  {
371  /* Start our timer */
372  mTimeStarted = PR_IntervalNow();
373  }
374  else if (oldstate == GST_STATE_PLAYING && newstate == GST_STATE_PAUSED)
375  {
377  mTimeStarted = (PRIntervalTime)-1;
378  }
379 
380  // Dispatch START, PAUSE, STOP events
381  if (pendingstate == GST_STATE_VOID_PENDING) {
382  if (newstate == GST_STATE_PLAYING)
384  else if (newstate == GST_STATE_PAUSED)
386  else if (newstate == GST_STATE_NULL)
388  }
389  }
390 }
391 
393  nsIVariant *aData, sbIMediacoreError *aError)
394 {
395  nsresult rv;
396  nsCOMPtr<sbIMediacoreEvent> event;
398  aError,
399  aData,
400  NULL,
401  getter_AddRefs(event));
402  NS_ENSURE_SUCCESS(rv, /* void */);
403 
404  rv = DispatchEvent(event, PR_TRUE, nsnull);
405  NS_ENSURE_SUCCESS(rv, /* void */);
406 }
407 
408 GstClockTime
410 {
411  PRIntervalTime now = PR_IntervalNow();
412  PRIntervalTime interval;
413 
414  if (mTimeStarted == (PRIntervalTime)-1)
415  return mTimeRunning;
416 
417  if (now < mTimeStarted) {
418  // Wraparound occurred, deal with it.
419  PRInt64 realnow = (PRInt64)now + ((PRInt64)1<<32);
420  interval = (PRIntervalTime)(realnow - mTimeStarted);
421  }
422  else {
423  interval = now - mTimeStarted;
424  }
425 
426  return mTimeRunning + PR_IntervalToMilliseconds (interval) * GST_MSECOND;
427 }
428 
429 // Forwarding functions for sbIMediacoreEventTarget interface
430 
431 NS_IMETHODIMP
432 sbGStreamerPipeline::DispatchEvent(sbIMediacoreEvent *aEvent,
433  PRBool aAsync,
434  PRBool* _retval)
435 {
436  return mBaseEventTarget ?
437  mBaseEventTarget->DispatchEvent(aEvent, aAsync, _retval) :
438  NS_ERROR_NULL_POINTER;
439 }
440 
441 NS_IMETHODIMP
442 sbGStreamerPipeline::AddListener(sbIMediacoreEventListener *aListener)
443 {
444  return mBaseEventTarget ?
445  mBaseEventTarget->AddListener(aListener) :
446  NS_ERROR_NULL_POINTER;
447 }
448 
449 NS_IMETHODIMP
450 sbGStreamerPipeline::RemoveListener(sbIMediacoreEventListener *aListener)
451 {
452  return mBaseEventTarget ?
453  mBaseEventTarget->RemoveListener(aListener) :
454  NS_ERROR_NULL_POINTER;
455 }
456 
Songbird Mediacore Error Definition.
return NS_OK
GStreamer::pipelineOp_t GetPipelineOp()
nsresult GetMediacoreErrorFromGstError(GError *gerror, nsString aResource, GStreamer::pipelineOp_t aPipelineOp, sbIMediacoreError **_retval)
id service()
nsAutoPtr< sbBaseMediacoreEventTarget > mBaseEventTarget
virtual void HandleBufferingMessage(GstMessage *message)
var event
NS_IMPL_THREADSAFE_CI(sbMediaListEnumeratorWrapper)
const unsigned long ERROR_EVENT
Indicates the event is an error and will have its error member set.
GstBusSyncReply SyncToAsyncDispatcher(GstBus *bus, GstMessage *message, gpointer data)
const unsigned long STREAM_STOP
Stream was stopped.
virtual void HandleErrorMessage(GstMessage *message)
NS_IMPL_THREADSAFE_RELEASE(sbRequestItem)
#define SBGSTREAMERSERVICE_CONTRACTID
NS_IMPL_THREADSAFE_ADDREF(sbRequestItem)
virtual void HandleWarningMessage(GstMessage *message)
Definition of the sbIMediacoreEvent interface.
const unsigned long STREAM_START
Stream has started.
const sbCreateProxiedComponent do_ProxiedGetService(const nsCID &aCID, nsresult *error=0)
virtual nsresult BuildPipeline()
void DispatchMediacoreEvent(unsigned long type, nsIVariant *aData=NULL, sbIMediacoreError *aError=NULL)
virtual void HandleEOSMessage(GstMessage *message)
virtual void HandleStateChangeMessage(GstMessage *message)
GstMessage * message
static nsresult CreateEvent(PRUint32 aType, sbIMediacoreError *aError, nsIVariant *aData, sbIMediacore *aOrigin, sbIMediacoreEvent **retval)
GStreamer::pipelineOp_t mPipelineOp
virtual void HandleMessage(GstMessage *message)
const unsigned long STREAM_PAUSE
Stream is now paused.
function debug(aMsg)
sbIJobCancelable NS_DECL_CLASSINFO(sbGstreamerMediaInspector)
virtual nsresult SetupPipeline()
void SetPipelineOp(GStreamer::pipelineOp_t aPipelineOp)
function now()
PRIntervalTime mTimeStarted
#define TRACE(args)
#define LOG(args)
NS_INTERFACE_MAP_END NS_IMPL_CI_INTERFACE_GETTER1(CDatabaseQuery, sbIDatabaseQuery) CDatabaseQuery
virtual nsresult OnDestroyPipeline(GstElement *pipeline)
virtual nsresult DestroyPipeline()
NS_IMPL_QUERY_INTERFACE2_CI(sbGStreamerPipeline, sbIMediacoreEventTarget, nsIClassInfo) NS_IMPL_CI_INTERFACE_GETTER1(sbGStreamerPipeline
virtual PRBool HandleSynchronousMessage(GstMessage *message)
_updateTextAndScrollDataForFrame aData
virtual void HandleTagMessage(GstMessage *message)