sbGStreamerMediaInspector.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-2009 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 
26 
27 #include <sbClassInfoUtils.h>
28 #include <sbStringBundle.h>
30 #include <sbStringUtils.h>
31 #include <sbStandardProperties.h>
33 
34 #include <sbIGStreamerService.h>
35 #include <sbIMediaItem.h>
36 #include <sbIMediaInspector.h>
37 #include <sbIMediaFormatMutable.h>
38 
39 #include <nsIWritablePropertyBag2.h>
40 #include <nsServiceManagerUtils.h>
41 #include <nsThreadUtils.h>
42 #include <prlog.h>
43 
44 /* Timeout for an inspectMedia() call, in ms */
45 #define GST_MEDIA_INSPECTOR_TIMEOUT (2000)
46 
47 #define GST_TYPE_PROPERTY "gst_type"
48 
53 #ifdef PR_LOGGING
54 static PRLogModuleInfo* gGStreamerMediaInspector = PR_NewLogModule("sbGStreamerMediaInspector");
55 #define LOG(args) PR_LOG(gGStreamerMediaInspector, PR_LOG_WARNING, args)
56 #define TRACE(args) PR_LOG(gGStreamerMediaInspector, PR_LOG_DEBUG, args)
57 #else /* PR_LOGGING */
58 #define LOG(args) /* nothing */
59 #define TRACE(args) /* nothing */
60 #endif /* PR_LOGGING */
61 
70 
71 NS_IMPL_CI_INTERFACE_GETTER4(sbGStreamerMediaInspector,
76 
77 NS_DECL_CLASSINFO(sbGstreamerMediaInspector);
78 NS_IMPL_THREADSAFE_CI(sbGStreamerMediaInspector);
79 
80 sbGStreamerMediaInspector::sbGStreamerMediaInspector() :
82  mStatus(sbIJobProgress::STATUS_RUNNING),
83  mFinished(PR_FALSE),
84  mIsPaused(PR_FALSE),
85  mTooComplexForCurrentImplementation(PR_FALSE),
86  mDecodeBin(NULL),
87  mVideoSrc(NULL),
88  mAudioSrc(NULL),
89  mAudioDecoderSink(NULL),
90  mVideoDecoderSink(NULL),
91  mDemuxerSink(NULL),
92  mAudioBitRate(0),
93  mVideoBitRate(0)
94 {
95  TRACE(("%s[%p]", __FUNCTION__, this));
96 }
97 
98 sbGStreamerMediaInspector::~sbGStreamerMediaInspector()
99 {
100  TRACE(("%s[%p]", __FUNCTION__, this));
101  /* destructor code */
102 }
103 
104 /* sbIJobCancelable interface implementation */
105 
106 NS_IMETHODIMP
107 sbGStreamerMediaInspector::GetCanCancel(PRBool *aCanCancel)
108 {
109  TRACE(("%s[%p]", __FUNCTION__, this));
110  NS_ENSURE_ARG_POINTER(aCanCancel);
111 
112  *aCanCancel = PR_TRUE;
113  return NS_OK;
114 }
115 
116 NS_IMETHODIMP
117 sbGStreamerMediaInspector::Cancel()
118 {
119  TRACE(("%s[%p]", __FUNCTION__, this));
120  mStatus = sbIJobProgress::STATUS_FAILED; // We don't have a 'cancelled' state.
121 
122  nsresult rv = StopPipeline();
123  NS_ENSURE_SUCCESS (rv, rv);
124 
125  return NS_OK;
126 }
127 
128 /* sbIJobProgress interface implementation */
129 
130 NS_IMETHODIMP
131 sbGStreamerMediaInspector::GetStatus(PRUint16 *aStatus)
132 {
133  TRACE(("%s[%p]", __FUNCTION__, this));
134  NS_ENSURE_ARG_POINTER(aStatus);
135 
136  *aStatus = mStatus;
137 
138  return NS_OK;
139 }
140 
141 NS_IMETHODIMP
142 sbGStreamerMediaInspector::GetBlocked(PRBool *aBlocked)
143 {
144  TRACE(("%s[%p]", __FUNCTION__, this));
145  NS_ENSURE_ARG_POINTER(aBlocked);
146 
147  *aBlocked = PR_FALSE;
148 
149  return NS_OK;
150 }
151 
152 NS_IMETHODIMP
153 sbGStreamerMediaInspector::GetStatusText(nsAString& aText)
154 {
155  TRACE(("%s[%p]", __FUNCTION__, this));
156  nsresult rv = NS_ERROR_FAILURE;
157 
158  switch (mStatus) {
160  rv = SBGetLocalizedString(aText,
161  NS_LITERAL_STRING("mediacore.gstreamer.inspect.failed"));
162  break;
164  rv = SBGetLocalizedString(aText,
165  NS_LITERAL_STRING("mediacore.gstreamer.inspect.succeeded"));
166  break;
168  rv = SBGetLocalizedString(aText,
169  NS_LITERAL_STRING("mediacore.gstreamer.inspect.running"));
170  break;
171  default:
172  NS_NOTREACHED("Status is invalid");
173  }
174 
175  return rv;
176 }
177 
178 NS_IMETHODIMP
179 sbGStreamerMediaInspector::GetTitleText(nsAString& aText)
180 {
181  TRACE(("%s[%p]", __FUNCTION__, this));
182  return SBGetLocalizedString(aText,
183  NS_LITERAL_STRING("mediacore.gstreamer.inspect.title"));
184 }
185 
186 NS_IMETHODIMP
187 sbGStreamerMediaInspector::GetProgress(PRUint32* aProgress)
188 {
189  TRACE(("%s[%p]", __FUNCTION__, this));
190  NS_ENSURE_ARG_POINTER(aProgress);
191 
192  *aProgress = 0; // Unknown
193  return NS_OK;
194 }
195 
196 NS_IMETHODIMP
197 sbGStreamerMediaInspector::GetTotal(PRUint32* aTotal)
198 {
199  TRACE(("%s[%p]", __FUNCTION__, this));
200  NS_ENSURE_ARG_POINTER(aTotal);
201 
202  *aTotal = 0;
203  return NS_OK;
204 }
205 
206 // Note that you can also get errors reported via the mediacore listener
207 // interfaces.
208 NS_IMETHODIMP
209 sbGStreamerMediaInspector::GetErrorCount(PRUint32* aErrorCount)
210 {
211  TRACE(("%s[%p]", __FUNCTION__, this));
212  NS_ENSURE_ARG_POINTER(aErrorCount);
213  NS_ASSERTION(NS_IsMainThread(),
214  "sbIJobProgress::GetErrorCount is main thread only!");
215 
216  *aErrorCount = mErrorMessages.Length();
217 
218  return NS_OK;
219 }
220 
221 NS_IMETHODIMP
222 sbGStreamerMediaInspector::GetErrorMessages(nsIStringEnumerator** aMessages)
223 {
224  TRACE(("%s[%p]", __FUNCTION__, this));
225  NS_ENSURE_ARG_POINTER(aMessages);
226  NS_ASSERTION(NS_IsMainThread(),
227  "sbIJobProgress::GetProgress is main thread only!");
228 
229  *aMessages = nsnull;
230 
231  nsCOMPtr<nsIStringEnumerator> enumerator =
232  new sbTArrayStringEnumerator(&mErrorMessages);
233  NS_ENSURE_TRUE(enumerator, NS_ERROR_OUT_OF_MEMORY);
234 
235  enumerator.forget(aMessages);
236  return NS_OK;
237 }
238 
239 NS_IMETHODIMP
240 sbGStreamerMediaInspector::AddJobProgressListener(sbIJobProgressListener *aListener)
241 {
242  TRACE(("%s[%p]", __FUNCTION__, this));
243  NS_ENSURE_ARG_POINTER(aListener);
244  NS_ASSERTION(NS_IsMainThread(),
245  "sbGStreamerMediaInspector::AddJobProgressListener is main thread only!");
246 
247  PRInt32 index = mProgressListeners.IndexOf(aListener);
248  if (index >= 0) {
249  // the listener already exists, do not re-add
250  return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
251  }
252  PRBool succeeded = mProgressListeners.AppendObject(aListener);
253  NS_ENSURE_TRUE(succeeded, NS_ERROR_FAILURE);
254 
255  return NS_OK;
256 }
257 
258 NS_IMETHODIMP
259 sbGStreamerMediaInspector::RemoveJobProgressListener(
260  sbIJobProgressListener* aListener)
261 {
262  TRACE(("%s[%p]", __FUNCTION__, this));
263  NS_ENSURE_ARG_POINTER(aListener);
264  NS_ASSERTION(NS_IsMainThread(),
265  "sbGStreamerMediaInspector::RemoveJobProgressListener is main thread only!");
266 
267  PRInt32 indexToRemove = mProgressListeners.IndexOf(aListener);
268  if (indexToRemove < 0) {
269  // No such listener, don't try to remove. This is OK.
270  return NS_OK;
271  }
272 
273  // remove the listener
274  PRBool succeeded = mProgressListeners.RemoveObjectAt(indexToRemove);
275  NS_ENSURE_TRUE(succeeded, NS_ERROR_FAILURE);
276 
277  return NS_OK;
278 }
279 
280 // Call all job progress listeners
281 nsresult
282 sbGStreamerMediaInspector::OnJobProgress()
283 {
284  TRACE(("%s[%p]", __FUNCTION__, this));
285  NS_ASSERTION(NS_IsMainThread(),
286  "sbGStreamerMediaInspector::OnJobProgress is main thread only!");
287 
288  // Announce our status to the world
289  for (PRInt32 i = mProgressListeners.Count() - 1; i >= 0; --i) {
290  // Ignore any errors from listeners
291  mProgressListeners[i]->OnJobProgress(this);
292  }
293  return NS_OK;
294 }
295 
296 void
297 sbGStreamerMediaInspector::HandleErrorMessage(GstMessage *message)
298 {
299  TRACE(("%s[%p]", __FUNCTION__, this));
300  GError *gerror = NULL;
301  gchar *debug = NULL;
302 
304 
305  gst_message_parse_error(message, &gerror, &debug);
306 
307  mErrorMessages.AppendElement(
308  NS_ConvertUTF8toUTF16(nsDependentCString(gerror->message)));
309 
310  g_error_free (gerror);
311  g_free(debug);
312 
313  nsresult rv = CompleteInspection();
314  NS_ENSURE_SUCCESS (rv, /* void */);
315 
316  // This will stop the pipeline and update listeners
318 }
319 
320 /* sbIGStreamerMediaInspector interface implementation */
321 
322 NS_IMETHODIMP
323 sbGStreamerMediaInspector::GetMediaFormat(sbIMediaFormat **_retval)
324 {
325  TRACE(("%s[%p]", __FUNCTION__, this));
326  NS_ENSURE_ARG_POINTER (_retval);
327 
328  if (mMediaFormat) {
329  nsresult rv = CallQueryInterface(mMediaFormat.get(), _retval);
330  NS_ENSURE_SUCCESS (rv, rv);
331  }
332  else {
333  return NS_ERROR_NOT_AVAILABLE;
334  }
335 
336  return NS_OK;
337 }
338 
339 NS_IMETHODIMP
340 sbGStreamerMediaInspector::InspectMediaURI(const nsAString & aURI,
341  sbIMediaFormat **_retval)
342 {
343  TRACE(("%s[%p]", __FUNCTION__, this));
344  NS_ENSURE_ARG_POINTER (_retval);
345 
346  nsresult rv = NS_ERROR_UNEXPECTED;
347  PRBool processed = PR_FALSE;
348  PRBool isMainThread = NS_IsMainThread();
349 
350  nsCOMPtr<nsIThread> target;
351  if (isMainThread) {
352  rv = NS_GetMainThread(getter_AddRefs(target));
353  NS_ENSURE_SUCCESS(rv, rv);
354  }
355 
356  NS_ASSERTION(!isMainThread,
357  "Synchronous InspectMedia is background-thread only");
358 
359  // Reset internal state tracking.
360  ResetStatus();
361 
362  rv = InspectMediaURIAsync (aURI);
363  NS_ENSURE_SUCCESS(rv, rv);
364 
365  while (PR_AtomicAdd (&mFinished, 0) == 0)
366  {
367  // This isn't something that's considered safe to do but it does
368  // the job right now. Ideally one would always want to use the
369  // async version that's usable on a background thread.
370  //
371  // The sync version is really meant to be used on a background thread!
372  // However, the Base Device currently needs this method to be synchronous
373  // and on the main thread. This will change in the future.
374  if (isMainThread && target) {
375  rv = target->ProcessNextEvent(PR_FALSE, &processed);
376  NS_ENSURE_SUCCESS(rv, rv);
377  }
378 
379  // This is an ugly hack, but works well enough.
380  PR_Sleep (50);
381  }
382 
383  if (mIsPaused && mMediaFormat) {
384  rv = CallQueryInterface(mMediaFormat.get(), _retval);
385  NS_ENSURE_SUCCESS (rv, rv);
386 
387  return NS_OK;
388  }
389  else {
390  // Otherwise we timed out or errored out; return an error since we were
391  // unable to determine anything about this media item.
392  return NS_ERROR_NOT_AVAILABLE;
393  }
394 }
395 
396 NS_IMETHODIMP
397 sbGStreamerMediaInspector::InspectMedia(sbIMediaItem *aMediaItem,
398  sbIMediaFormat **_retval)
399 {
400  TRACE(("%s[%p]", __FUNCTION__, this));
401  NS_ENSURE_ARG_POINTER (aMediaItem);
402  NS_ENSURE_ARG_POINTER (_retval);
403 
404  // Get the content location from the media item. (we don't use GetContentSrc
405  // because we want the string form, not a URI, and URI objects aren't
406  // threadsafe)
407  nsString sourceURI;
408  nsresult rv = aMediaItem->GetProperty(
409  NS_LITERAL_STRING(SB_PROPERTY_CONTENTURL), sourceURI);
410  NS_ENSURE_SUCCESS (rv, rv);
411 
412  // let InspectMediaURI do the work
413  return InspectMediaURI(sourceURI, _retval);
414 }
415 
416 NS_IMETHODIMP
417 sbGStreamerMediaInspector::InspectMediaURIAsync(const nsAString & aURI)
418 {
419  TRACE(("%s[%p]", __FUNCTION__, this));
420  mSourceURI = aURI;
421  // Reset internal state tracking.
422  ResetStatus();
423 
424  nsresult rv = StartTimeoutTimer();
425  NS_ENSURE_SUCCESS (rv, rv);
426 
427  // Set the pipeline to PAUSED. This will allow the pipeline to preroll,
428  // at which point we can inspect the caps on the pads, and look at the events
429  // that we have collected.
430  rv = PausePipeline();
431  NS_ENSURE_SUCCESS (rv, rv);
432 
433  return NS_OK;
434 }
435 
436 NS_IMETHODIMP
437 sbGStreamerMediaInspector::InspectMediaAsync(sbIMediaItem *aMediaItem)
438 {
439  TRACE(("%s[%p]", __FUNCTION__, this));
440  NS_ENSURE_ARG_POINTER (aMediaItem);
441 
442  // Get the content location from the media item. (we don't use GetContentSrc
443  // because we want the string form, not a URI, and URI objects aren't
444  // threadsafe)
445  nsString sourceURI;
446  nsresult rv = aMediaItem->GetProperty(
447  NS_LITERAL_STRING(SB_PROPERTY_CONTENTURL), sourceURI);
448  NS_ENSURE_SUCCESS (rv, rv);
449 
450  // let InspectMediaURIAsync do the work
451  return InspectMediaURIAsync(sourceURI);
452 }
453 
454 nsresult
455 sbGStreamerMediaInspector::CompleteInspection()
456 {
457  nsresult rv = StopTimeoutTimer();
458  NS_ENSURE_SUCCESS (rv, rv);
459 
460  // If we got to paused, inspect what we can from the pipeline
461  if (mIsPaused) {
462  rv = ProcessPipelineForInfo();
463  NS_ENSURE_SUCCESS (rv, rv);
464 
466  }
467  else {
468  // Otherwise we timed out or errored out; return an error since we were
469  // unable to determine anything about this media item.
470 
472  // Null this out since we failed.
473  mMediaFormat = NULL;
474  }
475 
476  mFinished = PR_TRUE;
477 
478  rv = StopPipeline();
479  NS_ENSURE_SUCCESS (rv, rv);
480 
481  return NS_OK;
482 }
483 
484 nsresult
485 sbGStreamerMediaInspector::StartTimeoutTimer()
486 {
487  TRACE(("%s[%p]", __FUNCTION__, this));
488  nsresult rv;
489 
490  // We want the timer to fire on the main thread, so we must create it on
491  // the main thread - this does so.
492  mTimeoutTimer = do_ProxiedCreateInstance("@mozilla.org/timer;1", &rv);
493  NS_ENSURE_SUCCESS(rv, rv);
494 
495  mTimeoutTimer->InitWithCallback(this,
497  nsITimer::TYPE_ONE_SHOT);
498 
499  return NS_OK;
500 }
501 
502 nsresult
503 sbGStreamerMediaInspector::StopTimeoutTimer()
504 {
505  TRACE(("%s[%p]", __FUNCTION__, this));
506  if (mTimeoutTimer) {
507  mTimeoutTimer->Cancel();
508  mTimeoutTimer = nsnull;
509  }
510 
511  return NS_OK;
512 }
513 
514 /* nsITimerCallback interface implementation */
515 
516 NS_IMETHODIMP
517 sbGStreamerMediaInspector::Notify(nsITimer *aTimer)
518 {
519  TRACE(("%s[%p]", __FUNCTION__, this));
520  NS_ENSURE_ARG_POINTER(aTimer);
521 
522  nsresult rv = CompleteInspection();
523  NS_ENSURE_SUCCESS (rv, rv);
524 
525  return NS_OK;
526 }
527 
528 
529 // Override base class to set the job progress appropriately
530 NS_IMETHODIMP
531 sbGStreamerMediaInspector::StopPipeline()
532 {
533  TRACE(("%s[%p]", __FUNCTION__, this));
534  nsresult rv;
535 
536  rv = sbGStreamerPipeline::StopPipeline();
537  NS_ENSURE_SUCCESS (rv, rv);
538 
539  // Inform listeners of new job status
540  rv = OnJobProgress();
541  NS_ENSURE_SUCCESS (rv, rv);
542 
543  rv = CleanupPipeline();
544  NS_ENSURE_SUCCESS (rv, rv);
545 
546  return NS_OK;
547 }
548 
549 nsresult
550 sbGStreamerMediaInspector::CleanupPipeline()
551 {
552  // Just clean up all our outstanding references to GStreamer objects.
553  if (mDecodeBin) {
554  g_object_unref (mDecodeBin);
555  mDecodeBin = NULL;
556  }
557  if (mVideoSrc) {
558  g_object_unref (mVideoSrc);
559  mVideoSrc = NULL;
560  }
561  if (mAudioSrc) {
562  g_object_unref (mAudioSrc);
563  mAudioSrc = NULL;
564  }
565  if (mAudioDecoderSink) {
566  g_object_unref (mAudioDecoderSink);
567  mAudioDecoderSink = NULL;
568  }
569  if (mVideoDecoderSink) {
570  g_object_unref (mVideoDecoderSink);
571  mVideoDecoderSink = NULL;
572  }
573  if (mDemuxerSink) {
574  g_object_unref (mDemuxerSink);
575  mDemuxerSink = NULL;
576  }
577 
578  return NS_OK;
579 }
580 
581 void
582 sbGStreamerMediaInspector::ResetStatus()
583 {
585  mFinished = PR_FALSE;
586  mIsPaused = PR_FALSE;
587  mTooComplexForCurrentImplementation = PR_FALSE;
588 }
589 
590 nsresult
591 sbGStreamerMediaInspector::BuildPipeline()
592 {
593  TRACE(("%s[%p]", __FUNCTION__, this));
594 
595  /* Build pipeline looking roughly like this (though either the audio or video
596  sides of this might be missing)
597  The audio/video parts are create and connected only after appropriate pads
598  are exposed by decodebin2 (if they exist at all), so initially our
599  pipeline contains only the source and the decodebin2.
600 
601  [-----------------------------------------------------------]
602  [media-inspector-pipeline ]
603  [ /[audio-queue]-[audio-fakesink] ]
604  [ [----------] [----------]/ ]
605  [ [uri-source]-[decodebin2]\ ]
606  [ [----------] [----------] \[video-queue]-[video-fakesink] ]
607  [ ]
608  [-----------------------------------------------------------]
609  */
610 
611  mPipeline = gst_pipeline_new ("media-inspector-pipeline");
612 
613  nsCString uri = NS_ConvertUTF16toUTF8 (mSourceURI);
614  GstElement *src = gst_element_make_from_uri (GST_URI_SRC,
615  uri.BeginReading(), "uri-source");
616 
617  if (!src) {
618  // TODO: Signal failure somehow with more info?
619  return NS_ERROR_FAILURE;
620  }
621 
622  mDecodeBin = gst_element_factory_make ("decodebin2", NULL);
623  // Take ownership of mDecodeBin via ref/sink
624  gst_object_ref (mDecodeBin);
625  gst_object_sink (mDecodeBin);
626 
627  // TODO: Connect up autoplug-sort signal to handle some special cases
628 // g_signal_connect (decodebin, "autoplug-sort",
629 // G_CALLBACK (decodebin_pad_added_cb), this);
630  g_signal_connect (mDecodeBin, "pad-added",
631  G_CALLBACK (decodebin_pad_added_cb), this);
632 
633  gst_bin_add_many (GST_BIN (mPipeline), src, mDecodeBin, NULL);
634 
635  GstPad *srcpad = gst_element_get_pad (src, "src");
636  GstPad *sinkpad = gst_element_get_pad (mDecodeBin, "sink");
637 
638  gst_pad_link (srcpad, sinkpad);
639 
640  g_object_unref (srcpad);
641  g_object_unref (sinkpad);
642 
644 
645  return NS_OK;
646 }
647 
648 nsresult
649 sbGStreamerMediaInspector::PadAdded(GstPad *srcpad)
650 {
651  TRACE(("%s[%p]", __FUNCTION__, this));
652 
653  // At this point, we may not have complete caps. However, we only want
654  // to identify audio vs. video vs. other, and the template caps returned by
655  // this will be sufficient.
656  // We don't look at the caps in detail until we are in the PAUSED state, at
657  // which point we explicitly look at the negotiated caps.
658  sbGstCaps caps = gst_pad_get_caps (srcpad);
659  GstStructure *structure = gst_caps_get_structure (caps, 0);
660  const gchar *name = gst_structure_get_name (structure);
661  bool isVideo = g_str_has_prefix (name, "video/");
662  bool isAudio = g_str_has_prefix (name, "audio/");
663 
664  if (isAudio && !mAudioSrc) {
665  GstElement *queue = gst_element_factory_make ("queue", "audio-queue");
666  GstElement *fakesink = gst_element_factory_make ("fakesink", "audio-sink");
667 
668  gst_bin_add_many (GST_BIN (mPipeline), queue, fakesink, NULL);
669  gst_element_sync_state_with_parent (queue);
670  gst_element_sync_state_with_parent (fakesink);
671 
672  GstPad *sinkpad = gst_element_get_pad (queue, "sink");
673 
674  gst_pad_link (srcpad, sinkpad);
675  g_object_unref (sinkpad);
676 
677  gst_element_link (queue, fakesink);
678 
679  GstPad *fakesinkpad = gst_element_get_pad (fakesink, "sink");
680  gst_pad_add_event_probe (fakesinkpad,
681  G_CALLBACK (fakesink_audio_event_cb), this);
682 
683  g_object_unref (fakesinkpad);
684 
685  mAudioSrc = GST_PAD (gst_object_ref (srcpad));
686  }
687  else if (isVideo && !mVideoSrc) {
688  GstElement *queue = gst_element_factory_make ("queue", "video-queue");
689  GstElement *fakesink = gst_element_factory_make ("fakesink", "video-sink");
690 
691  gst_bin_add_many (GST_BIN (mPipeline), queue, fakesink, NULL);
692  gst_element_sync_state_with_parent (queue);
693  gst_element_sync_state_with_parent (fakesink);
694 
695  GstPad *sinkpad = gst_element_get_pad (queue, "sink");
696 
697  gst_pad_link (srcpad, sinkpad);
698  g_object_unref (sinkpad);
699 
700  gst_element_link (queue, fakesink);
701 
702  GstPad *fakesinkpad = gst_element_get_pad (fakesink, "sink");
703  gst_pad_add_event_probe (fakesinkpad,
704  G_CALLBACK (fakesink_video_event_cb), this);
705 
706  g_object_unref (fakesinkpad);
707 
708  mVideoSrc = GST_PAD (gst_object_ref (srcpad));
709  }
710  else {
711  // Ignore this one.
712  }
713 
714  return NS_OK;
715 }
716 
717 nsresult
718 sbGStreamerMediaInspector::FakesinkEvent(GstPad *srcpad, GstEvent *event,
719  PRBool isAudio)
720 {
721  TRACE(("%s[%p]", __FUNCTION__, this));
722 
723  // Bit rate is already available.
724  if ((isAudio && mAudioBitRate) || (!isAudio && mVideoBitRate))
725  return NS_OK;
726 
727  guint bitrate = 0;
728 
729  switch (GST_EVENT_TYPE (event)) {
730  case GST_EVENT_TAG: {
731  GstTagList *list = NULL;
732 
733  gst_event_parse_tag (event, &list);
734  if (list && !gst_tag_list_is_empty (list)) {
735  gst_tag_list_get_uint (list, GST_TAG_BITRATE, &bitrate);
736  if (!bitrate)
737  gst_tag_list_get_uint (list, GST_TAG_NOMINAL_BITRATE, &bitrate);
738  }
739  break;
740  }
741  default:
742  break;
743  }
744 
745  if (bitrate) {
746  if (isAudio)
747  mAudioBitRate = bitrate;
748  else
749  mVideoBitRate = bitrate;
750  }
751 
752  return NS_OK;
753 }
754 
755 void
756 sbGStreamerMediaInspector::HandleStateChangeMessage(GstMessage *message)
757 {
758  TRACE(("%s[%p]", __FUNCTION__, this));
759 
761 
762  // Wait for the top-level pipeline to reach the PAUSED state, after letting
763  // our parent class handle the default stuff.
764  if (GST_IS_PIPELINE (message->src))
765  {
766  GstState oldstate, newstate, pendingstate;
767  gst_message_parse_state_changed (message,
768  &oldstate, &newstate, &pendingstate);
769 
770  if (pendingstate == GST_STATE_VOID_PENDING &&
771  newstate == GST_STATE_PAUSED)
772  {
773  mIsPaused = PR_TRUE;
774  nsresult rv = CompleteInspection();
775  NS_ENSURE_SUCCESS (rv, /* void */);
776  }
777  }
778 }
779 
780 nsresult
781 sbGStreamerMediaInspector::ProcessPipelineForInfo()
782 {
783  TRACE(("%s[%p]", __FUNCTION__, this));
784 
785  nsresult rv = NS_OK;
786 
787  // Ok, we've reached PAUSED. That means all our pads will have caps - so, we
788  // just need to look around for all the elements inside our decodebin, and
789  // figure out what exciting things we can find!
790  GstIterator *it = gst_bin_iterate_recurse (GST_BIN (mDecodeBin));
791  gboolean done = FALSE;
792 
793  while (!done) {
794  gpointer element;
795 
796  switch (gst_iterator_next (it, &element)) {
797  case GST_ITERATOR_OK:
798  rv = InspectorateElement (GST_ELEMENT (element));
799  gst_object_unref (element);
800  if (NS_FAILED (rv)) {
801  done = TRUE;
802  }
803  break;
804  case GST_ITERATOR_DONE:
805  done = TRUE;
806  break;
807  case GST_ITERATOR_RESYNC:
808  gst_iterator_resync (it);
809  break;
810  case GST_ITERATOR_ERROR:
811  done = TRUE;
812  rv = NS_ERROR_FAILURE;
813  break;
814  }
815  }
816 
817  gst_iterator_free (it);
818 
819  NS_ENSURE_SUCCESS (rv, rv);
820 
821  if (mAudioSrc) {
822  GstPad *audioSrcPad = GetRealPad (mAudioSrc);
823  GstElement *audioDecoder = GST_ELEMENT (gst_pad_get_parent (audioSrcPad));
824  GstElementFactory *factory = gst_element_get_factory (audioDecoder);
825  const gchar *klass = gst_element_factory_get_klass (factory);
826 
827  if (strstr (klass, "Decoder")) {
828  // Ok, it really is a decoder! Grab the sink pad to poke at in a bit.
829  mAudioDecoderSink = gst_element_get_pad (audioDecoder, "sink");
830  }
831 
832  g_object_unref (audioSrcPad);
833  g_object_unref (audioDecoder);
834  }
835 
836  if (mVideoSrc) {
837  GstPad *videoSrcPad = GetRealPad (mVideoSrc);
838  GstElement *videoDecoder = GST_ELEMENT (gst_pad_get_parent (videoSrcPad));
839  GstElementFactory *factory = gst_element_get_factory (videoDecoder);
840  const gchar *klass = gst_element_factory_get_klass (factory);
841 
842  if (strstr (klass, "Decoder")) {
843  // Ok, it really is a decoder! Grab the sink pad to poke at in a bit.
844  mVideoDecoderSink = gst_element_get_pad (videoDecoder, "sink");
845  }
846 
847  g_object_unref (videoSrcPad);
848  g_object_unref (videoDecoder);
849  }
850 
851  nsCOMPtr<sbIMediaFormatAudio> audioFormat;
852  nsCOMPtr<sbIMediaFormatVideo> videoFormat;
853  nsCOMPtr<sbIMediaFormatContainerMutable> containerFormat;
854 
855  if (mTooComplexForCurrentImplementation) {
856  // File info appears to be too complex to be understood/represented by
857  // the current implementation. Signal that with a special container type.
858  containerFormat = do_CreateInstance(SB_MEDIAFORMATCONTAINER_CONTRACTID,
859  &rv);
860  NS_ENSURE_SUCCESS (rv, rv);
861 
862  containerFormat->SetContainerType(NS_LITERAL_STRING("video/x-too-complex"));
863  }
864  else if (mDemuxerSink) {
865  // Ok, we have a demuxer - the container format should be described by
866  // the caps on this pad.
867  containerFormat = do_CreateInstance(SB_MEDIAFORMATCONTAINER_CONTRACTID,
868  &rv);
869  NS_ENSURE_SUCCESS (rv, rv);
870 
871  sbGstCaps caps = gst_pad_get_negotiated_caps (mDemuxerSink);
872  GstStructure *structure = gst_caps_get_structure (caps, 0);
873 
874  nsCString mimeType;
875  rv = GetMimeTypeForCaps (caps, mimeType);
876  NS_ENSURE_SUCCESS (rv, rv);
877 
878  rv = containerFormat->SetContainerType (NS_ConvertUTF8toUTF16(mimeType));
879  NS_ENSURE_SUCCESS (rv, rv);
880 
881  // format-specific attributes.
882  rv = ProcessContainerProperties(containerFormat, structure);
883  NS_ENSURE_SUCCESS (rv, rv);
884  }
885 
886  if (mVideoSrc) {
887  rv = ProcessVideo(getter_AddRefs(videoFormat));
888  NS_ENSURE_SUCCESS (rv, rv);
889  }
890 
891  if (mAudioSrc) {
892  rv = ProcessAudio(getter_AddRefs(audioFormat));
893  NS_ENSURE_SUCCESS (rv, rv);
894  }
895 
896  mMediaFormat = do_CreateInstance(SB_MEDIAFORMAT_CONTRACTID, &rv);
897  NS_ENSURE_SUCCESS (rv, rv);
898 
899  rv = mMediaFormat->SetContainer (containerFormat);
900  NS_ENSURE_SUCCESS (rv, rv);
901  rv = mMediaFormat->SetAudioStream (audioFormat);
902  NS_ENSURE_SUCCESS (rv, rv);
903  rv = mMediaFormat->SetVideoStream (videoFormat);
904 
905  return rv;
906 }
907 
908 nsresult
909 sbGStreamerMediaInspector::ProcessVideoCaps (sbIMediaFormatVideoMutable *format,
910  GstCaps *caps)
911 {
912  nsresult rv;
913  GstStructure *structure = gst_caps_get_structure (caps, 0);
914 
915  gint width, height;
916  if (gst_structure_get_int (structure, "width", &width) &&
917  gst_structure_get_int (structure, "height", &height))
918  {
919  rv = format->SetVideoWidth (width);
920  NS_ENSURE_SUCCESS (rv, rv);
921  rv = format->SetVideoHeight (height);
922  NS_ENSURE_SUCCESS (rv, rv);
923  }
924 
925  const GValue* framerate = gst_structure_get_value(structure, "framerate");
926  gint framerateN, framerateD;
927  if (framerate) {
928  framerateN = gst_value_get_fraction_numerator(framerate);
929  framerateD = gst_value_get_fraction_denominator(framerate);
930  }
931  else {
932  // Default to 0/1 indicating unknown framerate
933  framerateN = 0;
934  framerateD = 1;
935  }
936 
937  rv = format->SetVideoFrameRate (framerateN, framerateD);
938  NS_ENSURE_SUCCESS (rv, rv);
939 
940  const GValue* par = gst_structure_get_value(structure, "pixel-aspect-ratio");
941  gint parN, parD;
942  if (par) {
943  parN = gst_value_get_fraction_numerator(par);
944  parD = gst_value_get_fraction_denominator(par);
945  }
946  else {
947  // Default to square pixels
948  parN = 1;
949  parD = 1;
950  }
951 
952  rv = format->SetVideoPAR (parN, parD);
953  NS_ENSURE_SUCCESS (rv, rv);
954 
955  return NS_OK;
956 }
957 
958 nsresult
959 sbGStreamerMediaInspector::ProcessContainerProperties (
960  sbIMediaFormatContainerMutable *aContainerFormat,
961  GstStructure *aStructure)
962 {
963  TRACE(("%s[%p]", __FUNCTION__, this));
964 
965  NS_ENSURE_ARG_POINTER (aContainerFormat);
966  NS_ENSURE_ARG_POINTER (aStructure);
967 
968  nsresult rv;
969  const gchar *name = gst_structure_get_name (aStructure);
970  nsCOMPtr<nsIWritablePropertyBag2> writableBag =
971  do_CreateInstance("@songbirdnest.com/moz/xpcom/sbpropertybag;1", &rv);
972  NS_ENSURE_SUCCESS (rv, rv);
973  rv = writableBag->SetPropertyAsACString(NS_LITERAL_STRING(GST_TYPE_PROPERTY),
974  nsCString(name));
975  NS_ENSURE_SUCCESS (rv, rv);
976 
977  if ( !strcmp (name, "video/mpeg")) {
978  gboolean systemstream;
979  if ( gst_structure_get_boolean (aStructure, "systemstream",
980  &systemstream)) {
981  rv = writableBag->SetPropertyAsBool (
982  NS_LITERAL_STRING("systemstream"), systemstream);
983  NS_ENSURE_SUCCESS (rv, rv);
984  }
985  }
986 
987  // get total duration of media item
988  GstQuery *query;
989  query = gst_query_new_duration (GST_FORMAT_TIME);
990  gboolean res = gst_element_query (mPipeline, query);
991  // initialize to unknown
992  gint64 duration = -1;
993  if (res) {
994  gst_query_parse_duration (query, NULL, &duration);
995  }
996  gst_query_unref (query);
997  rv = writableBag->SetPropertyAsInt64 (
998  NS_LITERAL_STRING("duration"), duration);
999  NS_ENSURE_SUCCESS (rv, rv);
1000 
1001  // TODO: Additional properties for other container formats.
1002 
1003  rv = aContainerFormat->SetProperties (writableBag);
1004  NS_ENSURE_SUCCESS (rv, rv);
1005 
1006  return NS_OK;
1007 }
1008 
1009 nsresult
1010 sbGStreamerMediaInspector::ProcessVideoProperties (
1011  sbIMediaFormatVideoMutable *aVideoFormat,
1012  GstStructure *aStructure)
1013 {
1014  TRACE(("%s[%p]", __FUNCTION__, this));
1015 
1016  NS_ENSURE_ARG_POINTER (aVideoFormat);
1017  NS_ENSURE_ARG_POINTER (aStructure);
1018 
1019  nsresult rv;
1020  const gchar *name = gst_structure_get_name (aStructure);
1021  nsCOMPtr<nsIWritablePropertyBag2> writableBag =
1022  do_CreateInstance("@songbirdnest.com/moz/xpcom/sbpropertybag;1", &rv);
1023  NS_ENSURE_SUCCESS (rv, rv);
1024  rv = writableBag->SetPropertyAsACString(NS_LITERAL_STRING(GST_TYPE_PROPERTY),
1025  nsCString(name));
1026  NS_ENSURE_SUCCESS (rv, rv);
1027 
1028  if ( !strcmp (name, "video/mpeg")) {
1029  gint mpegversion;
1030  if ( gst_structure_get_int (aStructure, "mpegversion", &mpegversion)) {
1031  rv = writableBag->SetPropertyAsInt32 (
1032  NS_LITERAL_STRING("mpegversion"), mpegversion);
1033  NS_ENSURE_SUCCESS (rv, rv);
1034 
1035  // video/mpeg is NOT unique to mpeg4. also used for mpeg1/2.
1036  if (mpegversion == 4) {
1037  gint levelid;
1038  if ( gst_structure_get_int (aStructure, "profile-level-id",
1039  &levelid)) {
1040  rv = writableBag->SetPropertyAsInt32 (
1041  NS_LITERAL_STRING("profile-level-id"), levelid);
1042  NS_ENSURE_SUCCESS (rv, rv);
1043  }
1044  }
1045  }
1046  }
1047  else if ( !strcmp (name, "video/x-h264")) {
1048  // TODO: Additional property: profile ID?
1049  }
1050  else if ( !strcmp (name, "image/jpeg")) {
1051  gboolean interlaced;
1052  if ( gst_structure_get_boolean (aStructure, "interlaced", &interlaced)) {
1053  rv = writableBag->SetPropertyAsBool (
1054  NS_LITERAL_STRING("interlaced"), interlaced);
1055  NS_ENSURE_SUCCESS (rv, rv);
1056  }
1057  }
1058  else if ( !strcmp (name, "video/x-wmv")) {
1059  gint wmvversion;
1060  if ( gst_structure_get_int (aStructure, "wmvversion", &wmvversion)) {
1061  rv = writableBag->SetPropertyAsInt32 (
1062  NS_LITERAL_STRING("wmvversion"), wmvversion);
1063  NS_ENSURE_SUCCESS (rv, rv);
1064  }
1065  // TODO: Additional property: profile, level.
1066  }
1067  else if ( !strcmp (name, "video/x-pn-realvideo")) {
1068  gint rmversion;
1069  if ( gst_structure_get_int (aStructure, "rmversion", &rmversion)) {
1070  rv = writableBag->SetPropertyAsInt32 (
1071  NS_LITERAL_STRING("rmversion"), rmversion);
1072  NS_ENSURE_SUCCESS (rv, rv);
1073  }
1074  }
1075 
1076  rv = aVideoFormat->SetProperties (writableBag);
1077  NS_ENSURE_SUCCESS (rv, rv);
1078 
1079  return NS_OK;
1080 }
1081 
1082 nsresult
1083 sbGStreamerMediaInspector::ProcessVideo(sbIMediaFormatVideo **aVideoFormat)
1084 {
1085  TRACE(("%s[%p]", __FUNCTION__, this));
1086 
1087  NS_ENSURE_ARG_POINTER (aVideoFormat);
1088  NS_ENSURE_STATE (mVideoSrc);
1089 
1090  nsresult rv;
1091  nsCOMPtr<sbIMediaFormatVideoMutable> format =
1092  do_CreateInstance (SB_MEDIAFORMATVIDEO_CONTRACTID, &rv);
1093  NS_ENSURE_SUCCESS (rv, rv);
1094 
1095  // mVideoSrc is the decoded video pad from decodebin. We can process this for
1096  // information about the output video: resolution, framerate, etc.
1097  sbGstCaps caps = gst_pad_get_negotiated_caps (mVideoSrc);
1098  rv = ProcessVideoCaps(format, caps);
1099  NS_ENSURE_SUCCESS (rv, rv);
1100 
1101  rv = format->SetBitRate(mVideoBitRate);
1102  NS_ENSURE_SUCCESS (rv, rv);
1103 
1104  if (mVideoDecoderSink) {
1105  // This is the sink pad on the decoder. We can process this for
1106  // information about what codec is being used.
1107  // If we don't have a decoder sink pad, then that SHOULD mean that we have
1108  // raw video from the demuxer. Alternatively, it means we screwed up
1109  // somehow.
1110  sbGstCaps videoCaps = gst_pad_get_negotiated_caps (mVideoDecoderSink);
1111  GstStructure *structure = gst_caps_get_structure (videoCaps, 0);
1112 
1113  nsCString mimeType;
1114  rv = GetMimeTypeForCaps (videoCaps, mimeType);
1115  NS_ENSURE_SUCCESS (rv, rv);
1116 
1117  rv = format->SetVideoType (NS_ConvertUTF8toUTF16(mimeType));
1118  NS_ENSURE_SUCCESS (rv, rv);
1119 
1120  // format-specific attributes.
1121  rv = ProcessVideoProperties(format, structure);
1122  NS_ENSURE_SUCCESS (rv, rv);
1123  }
1124  else {
1125  // Raw video, mark as such.
1126  // TODO: Can we add any more checks in here to be sure it's ACTUALLY raw
1127  // video, and not just an internal error?
1128  rv = format->SetVideoType (NS_LITERAL_STRING ("video/x-raw"));
1129  NS_ENSURE_SUCCESS (rv, rv);
1130 
1131  // TODO: Add additional properties for raw video?
1132  }
1133 
1134  rv = CallQueryInterface(format.get(), aVideoFormat);
1135  NS_ENSURE_SUCCESS (rv, rv);
1136 
1137  return NS_OK;
1138 }
1139 
1140 
1141 nsresult
1142 sbGStreamerMediaInspector::ProcessAudioProperties (
1143  sbIMediaFormatAudioMutable *aAudioFormat,
1144  GstStructure *aStructure)
1145 {
1146  TRACE(("%s[%p]", __FUNCTION__, this));
1147 
1148  NS_ENSURE_ARG_POINTER (aAudioFormat);
1149  NS_ENSURE_ARG_POINTER (aStructure);
1150 
1151  nsresult rv;
1152  const gchar *name = gst_structure_get_name (aStructure);
1153  nsCOMPtr<nsIWritablePropertyBag2> writableBag =
1154  do_CreateInstance("@songbirdnest.com/moz/xpcom/sbpropertybag;1", &rv);
1155  NS_ENSURE_SUCCESS (rv, rv);
1156  rv = writableBag->SetPropertyAsACString(NS_LITERAL_STRING(GST_TYPE_PROPERTY),
1157  nsCString(name));
1158  NS_ENSURE_SUCCESS (rv, rv);
1159 
1160  if ( !strcmp (name, "audio/mpeg")) {
1161  gint mpegversion;
1162  if ( gst_structure_get_int (aStructure, "mpegversion", &mpegversion)) {
1163  rv = writableBag->SetPropertyAsInt32 (
1164  NS_LITERAL_STRING("mpegversion"), mpegversion);
1165  NS_ENSURE_SUCCESS (rv, rv);
1166 
1167  // MP3 audio
1168  if (mpegversion == 1) {
1169  gint layer;
1170  if ( gst_structure_get_int (aStructure, "layer",
1171  &layer)) {
1172  rv = writableBag->SetPropertyAsInt32 (
1173  NS_LITERAL_STRING("layer"), layer);
1174  NS_ENSURE_SUCCESS (rv, rv);
1175  }
1176  }
1177  else if (mpegversion == 2 || mpegversion == 4) {
1178  // TODO: Additional property: profile.
1179  }
1180  }
1181  }
1182  else if ( !strcmp (name, "audio/x-adpcm")) {
1183  const gchar *layout = gst_structure_get_string (aStructure, "layout");
1184  if (layout) {
1185  rv = writableBag->SetPropertyAsAString (
1186  NS_LITERAL_STRING("layout"), NS_ConvertUTF8toUTF16(layout));
1187  NS_ENSURE_SUCCESS (rv, rv);
1188  }
1189  }
1190  else if ( !strcmp (name, "audio/x-wma")) {
1191  gint wmaversion;
1192  if (gst_structure_get_int (aStructure, "wmaversion", &wmaversion)) {
1193  rv = writableBag->SetPropertyAsInt32 (
1194  NS_LITERAL_STRING("wmaversion"), wmaversion);
1195  NS_ENSURE_SUCCESS (rv, rv);
1196  }
1197  // TODO: Additional properties??
1198  }
1199  else if ( !strcmp (name, "audio/x-pn-realaudio")) {
1200  gint raversion;
1201  if (gst_structure_get_int (aStructure, "raversion", &raversion)) {
1202  rv = writableBag->SetPropertyAsInt32 (
1203  NS_LITERAL_STRING("raversion"), raversion);
1204  NS_ENSURE_SUCCESS (rv, rv);
1205  }
1206  }
1207 
1212 
1213  static const gchar * const INTERESTING_AUDIO_PROPS [] = {
1214  // pcm
1215  "width",
1216  "endianness",
1217  "signed",
1218 
1219  // aac
1220  "codec_data"
1221  };
1222 
1223  rv = SetPropertiesFromGstStructure(writableBag,
1224  aStructure,
1225  INTERESTING_AUDIO_PROPS,
1226  NS_ARRAY_LENGTH(INTERESTING_AUDIO_PROPS));
1227  NS_ENSURE_SUCCESS (rv, rv);
1228 
1229  rv = aAudioFormat->SetProperties (writableBag);
1230  NS_ENSURE_SUCCESS (rv, rv);
1231 
1232  return NS_OK;
1233 }
1234 
1235 nsresult
1236 sbGStreamerMediaInspector::ProcessAudio(sbIMediaFormatAudio **aAudioFormat)
1237 {
1238  TRACE(("%s[%p]", __FUNCTION__, this));
1239 
1240  NS_ENSURE_ARG_POINTER (aAudioFormat);
1241  NS_ENSURE_STATE (mAudioSrc);
1242 
1243  nsresult rv;
1244  nsCOMPtr<sbIMediaFormatAudioMutable> format =
1245  do_CreateInstance (SB_MEDIAFORMATAUDIO_CONTRACTID, &rv);
1246  NS_ENSURE_SUCCESS (rv, rv);
1247 
1248  // mAudioSrc is the decoded audio pad from decodebin. We can process this for
1249  // information about the output audio: sample rate, number of channels, etc.
1250  sbGstCaps caps = gst_pad_get_negotiated_caps (mAudioSrc);
1251  GstStructure *structure = gst_caps_get_structure (caps, 0);
1252 
1253  gint rate, channels;
1254  if (gst_structure_get_int (structure, "rate", &rate)) {
1255  format->SetSampleRate (rate);
1256  }
1257  if (gst_structure_get_int (structure, "channels", &channels)) {
1258  format->SetChannels (channels);
1259  }
1260 
1261  rv = format->SetBitRate(mAudioBitRate);
1262  NS_ENSURE_SUCCESS (rv, rv);
1263 
1264  if (mAudioDecoderSink) {
1265  // This is the sink pad on the decoder. We can process this for
1266  // information about what codec is being used.
1267  // If we don't have a decoder sink pad, then that SHOULD mean that we have
1268  // raw audio from the demuxer. Alternatively, it means we screwed up
1269  // somehow.
1270  sbGstCaps audioCaps = gst_pad_get_negotiated_caps (mAudioDecoderSink);
1271  structure = gst_caps_get_structure (audioCaps, 0);
1272 
1273  nsCString mimeType;
1274  rv = GetMimeTypeForCaps (audioCaps, mimeType);
1275  NS_ENSURE_SUCCESS (rv, rv);
1276 
1277  rv = format->SetAudioType (NS_ConvertUTF8toUTF16(mimeType));
1278  NS_ENSURE_SUCCESS (rv, rv);
1279  }
1280  else {
1281  // Raw audio, mark as such.
1282  // TODO: Can we add any more checks in here to be sure it's ACTUALLY raw
1283  // audio, and not just an internal error?
1284  format->SetAudioType (NS_LITERAL_STRING ("audio/x-raw"));
1285  }
1286 
1287  // format-specific attributes.
1288  rv = ProcessAudioProperties(format, structure);
1289  NS_ENSURE_SUCCESS (rv, rv);
1290 
1291  rv = CallQueryInterface(format.get(), aAudioFormat);
1292  NS_ENSURE_SUCCESS (rv, rv);
1293 
1294  return NS_OK;
1295 }
1296 
1297 nsresult
1298 sbGStreamerMediaInspector::InspectorateElement (GstElement *element)
1299 {
1300  TRACE(("%s[%p]", __FUNCTION__, this));
1301 
1302  GstElementFactory *factory = gst_element_get_factory (element);
1303 
1304  const gchar *klass = gst_element_factory_get_klass (factory);
1305 
1306  if (strstr (klass, "Demuxer")) {
1307  // Found what is hopefully our only demuxer!
1308  if (mDemuxerSink) {
1309  // No good! We don't support multiple demuxers.
1310  mTooComplexForCurrentImplementation = PR_TRUE;
1311  }
1312  else {
1313  mDemuxerSink = gst_element_get_pad (element, "sink");
1314  }
1315  }
1316 
1317  return NS_OK;
1318 }
1319 
1320 /* static */ void
1321 sbGStreamerMediaInspector::fakesink_audio_event_cb (GstPad * pad,
1322  GstEvent * event, sbGStreamerMediaInspector *inspector)
1323 {
1324  nsresult rv = inspector->FakesinkEvent(pad, event, PR_TRUE);
1325  NS_ENSURE_SUCCESS (rv, /* void */);
1326 }
1327 
1328 /* static */ void
1329 sbGStreamerMediaInspector::fakesink_video_event_cb (GstPad * pad,
1330  GstEvent * event, sbGStreamerMediaInspector *inspector)
1331 {
1332  nsresult rv = inspector->FakesinkEvent(pad, event, PR_FALSE);
1333  NS_ENSURE_SUCCESS (rv, /* void */);
1334 }
1335 
1336 /* static */ void
1337 sbGStreamerMediaInspector::decodebin_pad_added_cb (GstElement * uridecodebin,
1338  GstPad * pad, sbGStreamerMediaInspector *inspector)
1339 {
1340  nsresult rv = inspector->PadAdded(pad);
1341  NS_ENSURE_SUCCESS (rv, /* void */);
1342 }
1343 
return NS_OK
function succeeded(ch, cx, status, data)
NS_IMPL_THREADSAFE_CI(sbGStreamerMediaInspector)
GstPad * GetRealPad(GstPad *pad)
Generic interface for exposing long running jobs to the UI.
#define GST_MEDIA_INSPECTOR_TIMEOUT
var event
const unsigned short STATUS_SUCCEEDED
Constant indicating that the job has completed.
virtual void HandleErrorMessage(GstMessage *message)
function width(ele) rect(ele).width
const sbCreateProxiedComponent do_ProxiedCreateInstance(const nsCID &aCID, nsresult *error=0)
const unsigned short STATUS_RUNNING
Constant indicating that the job is active.
_hideDatepicker duration
#define SB_MEDIAFORMATCONTAINER_CONTRACTID
unique done
#define GST_TYPE_PROPERTY
virtual void HandleStateChangeMessage(GstMessage *message)
GstMessage * message
_updateDatepicker height
#define TRACE(args)
Songbird String Bundle Definitions.
#define SB_MEDIAFORMATVIDEO_CONTRACTID
nsresult SBGetLocalizedString(nsAString &aString, const nsAString &aKey, const nsAString &aDefault, class nsIStringBundle *aStringBundle)
var uri
Definition: FeedWriter.js:1135
function debug(aMsg)
nsresult GetMimeTypeForCaps(GstCaps *aCaps, nsACString &aMimeType)
sbIJobCancelable NS_DECL_CLASSINFO(sbGstreamerMediaInspector)
void SetPipelineOp(GStreamer::pipelineOp_t aPipelineOp)
Implemented to receive notifications from sbIJobProgress interfaces.
#define SB_MEDIAFORMAT_CONTRACTID
Interface that defines a single item of media in the system.
NS_IMPL_THREADSAFE_ISUPPORTS7(sbGStreamerMediaInspector, nsIClassInfo, sbIGStreamerPipeline, sbIMediacoreEventTarget, sbIMediaInspector, sbIJobProgress, sbIJobCancelable, nsITimerCallback) NS_IMPL_CI_INTERFACE_GETTER4(sbGStreamerMediaInspector
nsresult SetPropertiesFromGstStructure(nsIWritablePropertyBag2 *aPropertyBag, const GstStructure *aStructure, const gchar *const aDesiredFieldList[], PRUint32 aFieldCount)
#define SB_PROPERTY_CONTENTURL
const unsigned short STATUS_FAILED
Constant indicating that the job has completed with errors.
#define SB_MEDIAFORMATAUDIO_CONTRACTID
_getSelectedPageStyle s i
nsITimerCallback