sbGStreamerMediacoreUtils.cpp
Go to the documentation of this file.
1 /*
2 //
3 // BEGIN SONGBIRD GPL
4 //
5 // This file is part of the Songbird web player.
6 //
7 // Copyright(c) 2005-2008 POTI, Inc.
8 // http://songbirdnest.com
9 //
10 // This file may be licensed under the terms of of the
11 // GNU General Public License Version 2 (the "GPL").
12 //
13 // Software distributed under the License is distributed
14 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
15 // express or implied. See the GPL for the specific language
16 // governing rights and limitations.
17 //
18 // You should have received a copy of the GPL along with this
19 // program. If not, go to http://www.gnu.org/licenses/gpl.html
20 // or write to the Free Software Foundation, Inc.,
21 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 // END SONGBIRD GPL
24 //
25 */
26 
28 
29 #include <nsIRunnable.h>
30 #include <nsINetUtil.h>
31 #include <nsIWritablePropertyBag.h>
32 
33 #include <nsAutoPtr.h>
34 #include <nsThreadUtils.h>
35 #include <nsTArray.h>
36 #include <nsMemory.h>
37 
38 #include <sbStandardProperties.h>
39 #include <sbStringBundle.h>
40 
41 #include <prlog.h>
42 #include <gst/gst.h>
43 
50 #ifdef PR_LOGGING
51 
52 static PRLogModuleInfo* gGStreamerMediacoreUtils =
53  PR_NewLogModule("sbGStreamerMediacoreUtils");
54 
55 #define LOG(args) \
56  if (gGStreamerMediacoreUtils) \
57  PR_LOG(gGStreamerMediacoreUtils, PR_LOG_WARNING, args)
58 
59 #define TRACE(args) \
60  if (gGStreamerMediacoreUtils) \
61  PR_LOG(gGStreamerMediacore, PR_LOG_DEBUG, args)
62 
63 #else /* PR_LOGGING */
64 
65 #define LOG(args) /* nothing */
66 #define TRACE(args) /* nothing */
67 
68 #endif /* PR_LOGGING */
69 
70 // TODO: The following are known GStreamer tags we don't handle:
71 // GST_TAG_TITLE_SORTNAME
72 // GST_TAG_ARTIST_SORTNAME
73 // GST_TAG_ALBUM_SORTNAME
74 // GST_TAG_EXTENDED_COMMENT (probably not convertable?)
75 // GST_TAG_DESCRIPTION
76 // GST_TAG_VERSION
77 // GST_TAG_ISRC
78 // GST_TAG_ORGANIZATION
79 // GST_TAG_CONTACT
80 // GST_TAG_LICENSE
81 // GST_TAG_LICENSE_URI
82 // GST_TAG_PERFORMER
83 // GST_TAG_SERIAL
84 // GST_TAG_TRACK_GAIN
85 // GST_TAG_TRACK_PEAK
86 // GST_TAG_ALBUM_GAIN
87 // GST_TAG_ALBUM_PEAK
88 // GST_TAG_REFERENCE_LEVEL
89 // GST_TAG_LANGUAGE_CODE
90 // GST_TAG_IMAGE
91 // GST_TAG_PREVIEW_IMAGE
92 // GST_TAG_ATTACHMENT
93 // GST_TAG_BEATS_PER_MINUTE
94 // GST_TAG_CODEC
95 // GST_TAG_VIDEO_CODEC
96 // GST_TAG_AUDIO_CODEC
97 // GST_TAG_BITRATE
98 // GST_TAG_NOMINAL_BITRATE
99 // GST_TAG_MINIMUM_BITRATE
100 // GST_TAG_MAXIMUM_BITRATE
101 // GST_TAG_ENCODER
102 // GST_TAG_ENCODER_VERSION
103 
104 // Property names for Gracenote properties
105 // These must match the definitions in
106 // extras/extensions/gracenote/src/sbGracenoteDefines.h
107 #define SB_GN_PROP_EXTENDEDDATA "http://gracenote.com/pos/1.0#extendedData"
108 #define SB_GN_PROP_TAGID "http://gracenote.com/pos/1.0#tagId"
109 
110 PRBool
112  GstTagList *taglist)
113 {
114  nsresult rv;
115  nsString id, value;
116 
117  rv = property->GetId(id);
118  NS_ENSURE_SUCCESS(rv,PR_FALSE);
119 
120  rv = property->GetValue(value);
121  NS_ENSURE_SUCCESS(rv,PR_FALSE);
122 
123 /* Some nasty macros. Less nasty than writing all this out any other way... */
124 #define TAG_CONVERT_STRING(sbname,gstname) \
125  if (id == NS_LITERAL_STRING (sbname)) { \
126  NS_ConvertUTF16toUTF8 utf8value(value); \
127  gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, (gchar *)gstname, \
128  utf8value.BeginReading(), NULL); \
129  return PR_TRUE; \
130  }
131 
132 #define TAG_CONVERT_UINT(sbname,gstname) \
133  if (id == NS_LITERAL_STRING (sbname)) { \
134  unsigned int gstvalue = value.ToInteger(&rv); \
135  NS_ENSURE_SUCCESS (rv, PR_FALSE); \
136  gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, (gchar *)gstname, \
137  gstvalue, NULL); \
138  return PR_TRUE; \
139  }
140 
141  TAG_CONVERT_STRING (SB_PROPERTY_ALBUMNAME, GST_TAG_ALBUM);
142  TAG_CONVERT_STRING (SB_PROPERTY_ARTISTNAME, GST_TAG_ARTIST);
143  TAG_CONVERT_STRING (SB_PROPERTY_TRACKNAME, GST_TAG_TITLE);
144  TAG_CONVERT_STRING (SB_PROPERTY_COMPOSERNAME, GST_TAG_COMPOSER);
145  TAG_CONVERT_STRING (SB_PROPERTY_GENRE, GST_TAG_GENRE);
146  TAG_CONVERT_STRING (SB_PROPERTY_COMMENT, GST_TAG_COMMENT);
147  TAG_CONVERT_STRING (SB_PROPERTY_ORIGINURL, GST_TAG_LOCATION);
148  TAG_CONVERT_STRING (SB_PROPERTY_COPYRIGHT, GST_TAG_COPYRIGHT);
149  TAG_CONVERT_STRING (SB_PROPERTY_COPYRIGHTURL, GST_TAG_COPYRIGHT_URI);
150 
151  TAG_CONVERT_UINT (SB_PROPERTY_TRACKNUMBER, GST_TAG_TRACK_NUMBER);
152  TAG_CONVERT_UINT (SB_PROPERTY_TOTALTRACKS, GST_TAG_TRACK_COUNT);
153  TAG_CONVERT_UINT (SB_PROPERTY_DISCNUMBER, GST_TAG_ALBUM_VOLUME_NUMBER);
154  TAG_CONVERT_UINT (SB_PROPERTY_TOTALDISCS, GST_TAG_ALBUM_VOLUME_COUNT);
155 
156  // Some special cases, without the macros...
157  if (id == NS_LITERAL_STRING (SB_PROPERTY_YEAR)) {
158  GDate *date;
159  int year = value.ToInteger(&rv);
160  NS_ENSURE_SUCCESS (rv, PR_FALSE);
161 
162  date = g_date_new();
163  g_date_set_year (date, year);
164  gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, (gchar *)GST_TAG_DATE,
165  date, NULL);
166  g_date_free (date);
167  return PR_TRUE;
168  }
169 
170  if (id == NS_LITERAL_STRING (SB_PROPERTY_DURATION)) {
171  int durationMillis = value.ToInteger(&rv);
172  guint64 durationNanos;
173  NS_ENSURE_SUCCESS (rv, PR_FALSE);
174  durationNanos = durationMillis * GST_MSECOND;
175  gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, (gchar *)GST_TAG_DURATION,
176  durationNanos, NULL);
177  return PR_TRUE;
178  }
179 
180  /* Custom stuff we're contractually obligated to include. There's no clean
181  way to do this. */
186 
187  // If we get here, we failed to convert it.
188  return PR_FALSE;
189 }
190 
191 GstTagList *
193 {
194  nsresult rv;
195  nsCOMPtr<nsISimpleEnumerator> propertyEnum;
196  PRBool more;
197  GstTagList *tags;
198  PRBool converted;
199 
200  if (properties == nsnull)
201  return NULL;
202 
203  tags = gst_tag_list_new();
204  rv = properties->Enumerate(getter_AddRefs(propertyEnum));
205  NS_ENSURE_SUCCESS(rv, NULL);
206 
207  while (NS_SUCCEEDED (propertyEnum->HasMoreElements(&more)) && more) {
208  nsCOMPtr<nsISupports> next;
209  if (NS_SUCCEEDED(propertyEnum->GetNext(getter_AddRefs(next))) && next) {
210  nsCOMPtr<sbIProperty> property(do_QueryInterface(next));
211 
212  converted = ConvertSinglePropertyToTag (property, tags);
213  if (!converted) {
214  LOG(("Failed to convert property to tag"));
215  }
216  }
217  }
218 
219  return tags;
220 }
221 
222 static void
223 ConvertSingleTag(const GstTagList *taglist,
224  const gchar *tag, gpointer user_data)
225 {
226  sbIMutablePropertyArray *propArray =
227  reinterpret_cast<sbIMutablePropertyArray *>(user_data);
228 
229 #define PROPERTY_CONVERT_STRING(propname, tagname) \
230  if (!strcmp(tag, tagname)) { \
231  gchar *tagvalue; \
232  if (gst_tag_list_get_string (taglist, tag, &tagvalue)) { \
233  propArray->AppendProperty(NS_LITERAL_STRING (propname), \
234  NS_ConvertUTF8toUTF16(tagvalue)); \
235  g_free (tagvalue); \
236  return; \
237  } \
238  }
239 
240 #define PROPERTY_CONVERT_UINT(propname, tagname, scale) \
241  if (!strcmp(tag, tagname)) { \
242  guint tagvalue; \
243  if (gst_tag_list_get_uint (taglist, tag, &tagvalue)) { \
244  nsString stringVal; \
245  stringVal.AppendInt(tagvalue / scale); \
246  propArray->AppendProperty(NS_LITERAL_STRING (propname), \
247  stringVal); \
248  return; \
249  } \
250  }
251 
257  PROPERTY_CONVERT_STRING (SB_PROPERTY_COMMENT, GST_TAG_COMMENT);
259  PROPERTY_CONVERT_STRING (SB_PROPERTY_COPYRIGHT, GST_TAG_COPYRIGHT);
260  PROPERTY_CONVERT_STRING (SB_PROPERTY_COPYRIGHTURL, GST_TAG_COPYRIGHT_URI);
261 
262  PROPERTY_CONVERT_UINT (SB_PROPERTY_BITRATE, GST_TAG_BITRATE, 1000);
263 }
264 
265 nsresult
266 ConvertTagListToPropertyArray(GstTagList *taglist,
267  sbIPropertyArray **aPropertyArray)
268 {
269  nsresult rv;
270  nsCOMPtr<sbIMutablePropertyArray> proparray = do_CreateInstance(
271  "@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1", &rv);
272  NS_ENSURE_SUCCESS (rv, rv);
273 
274  gst_tag_list_foreach (taglist, ConvertSingleTag, proparray);
275 
276  nsCOMPtr<sbIPropertyArray> props = do_QueryInterface(proparray, &rv);
277  NS_ENSURE_SUCCESS (rv, rv);
278 
279  NS_ADDREF(*aPropertyArray = props);
280  return NS_OK;
281 }
282 
283 class sbGstMessageEvent : public nsIRunnable
284 {
285 public:
287 
289  mHandler(handler)
290  {
291  gst_message_ref(msg);
292  mMessage = msg;
293  }
294 
296  gst_message_unref(mMessage);
297  }
298 
299  NS_IMETHOD Run()
300  {
301  mHandler->HandleMessage(mMessage);
302  return NS_OK;
303  }
304 
305 private:
306  GstMessage *mMessage;
307  nsRefPtr<sbGStreamerMessageHandler> mHandler;
308 };
309 
311  nsIRunnable)
312 
313 GstBusSyncReply
314 SyncToAsyncDispatcher(GstBus* bus, GstMessage* message, gpointer data)
315 {
317  reinterpret_cast<sbGStreamerMessageHandler*>(data);
318 
319  // Allow a sync handler to look at this first.
320  // If it returns false (the default), we dispatch it asynchronously.
321  PRBool handled = handler->HandleSynchronousMessage(message);
322 
323  if (!handled) {
324  nsCOMPtr<nsIRunnable> event = new sbGstMessageEvent(message, handler);
325  NS_DispatchToMainThread(event);
326  }
327 
328  gst_message_unref (message);
329 
330  return GST_BUS_DROP;
331 }
332 
333 struct errMap {
337  const char *sbErrorMessageName;
338 };
339 
340 static const struct errMap ResourceErrorMap[] = {
341  { GST_RESOURCE_ERROR_NOT_FOUND, GStreamer::OP_UNKNOWN,
343  "mediacore.error.resource_not_found"},
344  { GST_RESOURCE_ERROR_READ, GStreamer::OP_UNKNOWN,
346  "mediacore.error.read_failed"},
347  { GST_RESOURCE_ERROR_WRITE, GStreamer::OP_UNKNOWN,
349  "mediacore.error.write_failed"},
350  { GST_RESOURCE_ERROR_OPEN_READ, GStreamer::OP_UNKNOWN,
352  "mediacore.error.read_open_failed"},
353  { GST_RESOURCE_ERROR_OPEN_WRITE, GStreamer::OP_UNKNOWN,
355  "mediacore.error.write_open_failed"},
356  { GST_RESOURCE_ERROR_OPEN_READ_WRITE, GStreamer::OP_UNKNOWN,
358  "mediacore.error.rw_open_failed"},
359  { GST_RESOURCE_ERROR_CLOSE, GStreamer::OP_UNKNOWN,
361  "mediacore.error.close_failed"},
362  { GST_RESOURCE_ERROR_SEEK, GStreamer::OP_UNKNOWN,
364  "mediacore.error.seek_failed"},
365  { GST_RESOURCE_ERROR_NO_SPACE_LEFT, GStreamer::OP_UNKNOWN,
367  "mediacore.error.out_of_space"},
368 };
369 
370 // No error codes for these yet
371 #if 0
372 static const struct errMap CoreErrorMap[] = {
373 };
374 
375 static const struct errMap LibraryErrorMap[] = {
376 };
377 #endif
378 
379 static const struct errMap StreamErrorMap[] = {
380  { GST_STREAM_ERROR_TYPE_NOT_FOUND, GStreamer::OP_UNKNOWN,
382  "mediacore.error.type_not_found"},
383  { GST_STREAM_ERROR_WRONG_TYPE, GStreamer::OP_UNKNOWN,
385  "mediacore.error.wrong_type"},
386  { GST_STREAM_ERROR_CODEC_NOT_FOUND, GStreamer::OP_UNKNOWN,
388  "mediacore.error.codec_not_found"},
389  { GST_STREAM_ERROR_DECODE, GStreamer::OP_TRANSCODING,
391  "mediacore.error.decode_for_transcode_failed" },
392  { GST_STREAM_ERROR_DECODE, GStreamer::OP_UNKNOWN,
394  "mediacore.error.decode_failed"},
395  { GST_STREAM_ERROR_ENCODE, GStreamer::OP_UNKNOWN,
397  "mediacore.error.encode_failed"},
398  { GST_STREAM_ERROR_FAILED, GStreamer::OP_TRANSCODING,
400  "mediacore.error.failure_during_transcode"},
401  { GST_STREAM_ERROR_FAILED, GStreamer::OP_UNKNOWN,
403  "mediacore.error.failure"},
404  { GST_STREAM_ERROR_DECRYPT, GStreamer::OP_UNKNOWN,
406  "mediacore.error.decrypt"},
407 };
408 
409 nsresult
410 GetMediacoreErrorFromGstError(GError *gerror, nsString aResource,
411  GStreamer::pipelineOp_t aPipelineOp,
412  sbIMediacoreError **_retval)
413 {
414  nsString errorMessage;
415  nsRefPtr<sbMediacoreError> error;
416  int errorCode = 0;
417  const char *stringName = NULL;
418  nsresult rv;
419 
420  NS_NEWXPCOM(error, sbMediacoreError);
421  NS_ENSURE_TRUE (error, NS_ERROR_OUT_OF_MEMORY);
422 
423  const struct errMap *map = NULL;
424  int mapLength;
425 #if 0
426  if (gerror->domain == GST_CORE_ERROR) {
427  map = CoreErrorMap;
428  mapLength = NS_ARRAY_LENGTH(CoreErrorMap);
429  }
430  else if (gerror->domain == GST_LIBRARY_ERROR) {
431  map = LibraryErrorMap;
432  mapLength = NS_ARRAY_LENGTH(LibraryErrorMap);
433  }
434  else
435 #endif
436  if (gerror->domain == GST_RESOURCE_ERROR) {
437  map = ResourceErrorMap;
438  mapLength = NS_ARRAY_LENGTH(ResourceErrorMap);
439  }
440  else if (gerror->domain == GST_STREAM_ERROR) {
441  map = StreamErrorMap;
442  mapLength = NS_ARRAY_LENGTH(StreamErrorMap);
443  }
444 
445  if (map) {
446  int match = -1;
447  int bestMatch = -1;
448 
449  for (int i = 0; i < mapLength; i++) {
450  if (map[i].gstErrorCode == gerror->code) {
451  // Try and find an exact match.
452  if (aPipelineOp != GStreamer::OP_UNKNOWN &&
453  map[i].gstPipelineOp == aPipelineOp) {
454  bestMatch = i;
455  // bestMatch found, break out right away.
456  break;
457  }
458  else {
459  // We can't break out here because the bestMatch
460  // may be coming later in the map.
461  match = i;
462  }
463  }
464  }
465 
466  if (bestMatch == -1 && match != -1) {
467  bestMatch = match;
468  }
469 
470  if (bestMatch != -1) {
471  errorCode = map[bestMatch].sbErrorCode;
472  stringName = map[bestMatch].sbErrorMessageName;
473  }
474  }
475 
476  if (stringName) {
478  nsTArray<nsString> params;
479  if (aResource.IsEmpty()) {
480  params.AppendElement(bundle.Get("mediacore.error.unknown_resource"));
481  }
482  else {
483  // Unescape the resource.
484  nsCOMPtr<nsINetUtil> netUtil =
485  do_CreateInstance("@mozilla.org/network/util;1", &rv);
486  NS_ENSURE_SUCCESS(rv, rv);
487  nsCString unescapedResource;
488  rv = netUtil->UnescapeString(NS_ConvertUTF16toUTF8(aResource),
489  nsINetUtil::ESCAPE_ALL, unescapedResource);
490  NS_ENSURE_SUCCESS(rv, rv);
491 
492  params.AppendElement(NS_ConvertUTF8toUTF16(unescapedResource));
493  }
494 
495  errorMessage = bundle.Format(stringName, params);
496  }
497 
498  if (errorMessage.IsEmpty()) {
499  // If we get here just fall back to the gerror's internal message,
500  // untranslated.
501  CopyUTF8toUTF16(nsDependentCString(gerror->message), errorMessage);
502  }
503 
504  rv = error->Init(errorCode, errorMessage);
505  NS_ENSURE_SUCCESS (rv, rv);
506 
507  NS_ADDREF (*_retval = error);
508 
509  return NS_OK;
510 }
511 
512 typedef struct {
513  GstCaps *srccaps;
514  const char *type;
516 
517 static gboolean
518 match_element_filter (GstPluginFeature * feature, TypeMatchingInfo * data)
519 {
520  const gchar *klass;
521  const GList *templates;
522  GList *walk;
523  GstElementFactory * factory;
524  const char *name;
525 
526  /* we only care about element factories */
527  if (!GST_IS_ELEMENT_FACTORY (feature))
528  return FALSE;
529 
530  factory = GST_ELEMENT_FACTORY (feature);
531 
532  klass = gst_element_factory_get_klass (factory);
533 
534  if (strstr (klass, data->type) == NULL) {
535  /* Wrong type, don't check further */
536  return FALSE;
537  }
538 
539  /* Blacklist ffmux and ffenc. We don't want to accidently use these on
540  linux systems where they might be loaded from the system. */
541  name = gst_plugin_feature_get_name (feature);
542  if (strstr (name, "ffmux") != NULL ||
543  strstr (name, "ffenc") != NULL)
544  return FALSE;
545 
546  templates = gst_element_factory_get_static_pad_templates (factory);
547  for (walk = (GList *) templates; walk; walk = g_list_next (walk)) {
548  GstStaticPadTemplate *templ = (GstStaticPadTemplate *)(walk->data);
549 
550  /* Only want source pad templates */
551  if (templ->direction == GST_PAD_SRC) {
552  GstCaps *template_caps = gst_static_caps_get (&templ->static_caps);
553  GstCaps *intersect;
554 
555  intersect = gst_caps_intersect (template_caps, data->srccaps);
556  gst_caps_unref (template_caps);
557 
558  if (!gst_caps_is_empty (intersect)) {
559  // Non-empty intersection: caps are usable
560  gst_caps_unref (intersect);
561  return TRUE;
562  }
563  gst_caps_unref (intersect);
564  }
565  }
566 
567  /* Didn't find a compatible pad */
568  return FALSE;
569 }
570 
571 const char *
572 FindMatchingElementName(const char *srcCapsString, const char *typeName)
573 {
574  GstCaps *caps;
575 
576  caps = gst_caps_from_string (srcCapsString);
577  if (!caps)
578  return NULL;
579 
580  const char* result = FindMatchingElementName(caps, typeName);
581  gst_caps_unref(caps);
582  return result;
583 }
584 
585 const char *
586 FindMatchingElementName(GstCaps *srcCaps, const char *typeName)
587 {
588  GList *list, *walk;
590  guint bestrank = 0;
591  GstElementFactory *bestfactory = NULL;
592 
593  if (!srcCaps)
594  return NULL;
595 
596  data.srccaps = srcCaps;
597  data.type = typeName;
598 
599  list = gst_default_registry_feature_filter (
600  (GstPluginFeatureFilter)match_element_filter, FALSE, &data);
601 
602  for (walk = list; walk; walk = g_list_next (walk)) {
603  GstElementFactory *factory = GST_ELEMENT_FACTORY (walk->data);
604  guint rank = gst_plugin_feature_get_rank (GST_PLUGIN_FEATURE (factory));
605 
606  // Find the highest-ranked element that we considered acceptable.
607  if (!bestfactory || rank > bestrank) {
608  bestfactory = factory;
609  bestrank = rank;
610  }
611  }
612 
613  if (!bestfactory)
614  return NULL;
615 
616  return gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (bestfactory));
617 }
618 
619 void
621 {
622  gst_tag_register (SB_GST_TAG_GRACENOTE_TAGID, GST_TAG_FLAG_META,
623  G_TYPE_STRING, "GN TagID", "Gracenote Tag ID", NULL);
624  gst_tag_register (SB_GST_TAG_GRACENOTE_EXTENDED_DATA, GST_TAG_FLAG_META,
625  G_TYPE_STRING, "GN ExtData", "Gracenote Extended Data", NULL);
626 }
627 
628 nsresult
629 ApplyPropertyBagToElement(GstElement *element, nsIPropertyBag *props)
630 {
631  nsCOMPtr<nsISimpleEnumerator> enumerator;
632  nsresult rv = props->GetEnumerator (getter_AddRefs (enumerator));
633  NS_ENSURE_SUCCESS (rv, rv);
634 
635  PRBool hasMore = PR_FALSE;
636  while (NS_SUCCEEDED (enumerator->HasMoreElements (&hasMore)) && hasMore) {
637  nsCOMPtr<nsISupports> next;
638  rv = enumerator->GetNext (getter_AddRefs (next));
639  NS_ENSURE_SUCCESS (rv, rv);
640 
641  nsCOMPtr<nsIProperty> property = do_QueryInterface (next, &rv);
642  NS_ENSURE_SUCCESS (rv, rv);
643 
644  nsString propertyName;
645  rv = property->GetName (propertyName);
646  NS_ENSURE_SUCCESS (rv, rv);
647 
648  nsCOMPtr<nsIVariant> propertyVariant;
649  rv = property->GetValue (getter_AddRefs (propertyVariant));
650  NS_ENSURE_SUCCESS (rv, rv);
651 
652  NS_ConvertUTF16toUTF8 propertyNameUTF8 (propertyName);
653 
654  GParamSpec *paramSpec = g_object_class_find_property (
655  G_OBJECT_GET_CLASS (element),
656  propertyNameUTF8.BeginReading());
657  if (!paramSpec)
658  {
659  LOG(("No such property %s on element", propertyNameUTF8.BeginReading()));
660  return NS_ERROR_FAILURE;
661  }
662 
663  PRUint16 variantType;
664  rv = propertyVariant->GetDataType(&variantType);
665  NS_ENSURE_SUCCESS (rv, rv);
666 
667  GValue propertyValue = { 0 };
668  if (paramSpec->value_type == G_TYPE_INT)
669  {
670  PRInt32 val;
671  rv = propertyVariant->GetAsInt32 (&val);
672  NS_ENSURE_SUCCESS (rv, rv);
673  g_value_init (&propertyValue, G_TYPE_INT);
674  g_value_set_int (&propertyValue, val);
675  }
676  else if (paramSpec->value_type == G_TYPE_UINT)
677  {
678  PRUint32 val;
679  rv = propertyVariant->GetAsUint32 (&val);
680  NS_ENSURE_SUCCESS (rv, rv);
681  g_value_init (&propertyValue, G_TYPE_UINT);
682  g_value_set_uint (&propertyValue, val);
683  }
684  else if (paramSpec->value_type == G_TYPE_UINT64)
685  {
686  PRUint64 val;
687  rv = propertyVariant->GetAsUint64 (&val);
688  NS_ENSURE_SUCCESS (rv, rv);
689  g_value_init (&propertyValue, G_TYPE_UINT64);
690  g_value_set_uint64 (&propertyValue, val);
691  }
692  else if (paramSpec->value_type == G_TYPE_INT64)
693  {
694  PRInt64 val;
695  rv = propertyVariant->GetAsInt64 (&val);
696  NS_ENSURE_SUCCESS (rv, rv);
697  g_value_init (&propertyValue, G_TYPE_INT64);
698  g_value_set_int64 (&propertyValue, val);
699  }
700  else if (paramSpec->value_type == G_TYPE_BOOLEAN)
701  {
702  PRBool val;
703  rv = propertyVariant->GetAsBool (&val);
704  NS_ENSURE_SUCCESS (rv, rv);
705  g_value_init (&propertyValue, G_TYPE_BOOLEAN);
706  g_value_set_boolean (&propertyValue, val);
707  }
708  else if (paramSpec->value_type == G_TYPE_FLOAT)
709  {
710  float val;
711  rv = propertyVariant->GetAsFloat (&val);
712  NS_ENSURE_SUCCESS (rv, rv);
713  g_value_init (&propertyValue, G_TYPE_FLOAT);
714  g_value_set_float (&propertyValue, val);
715  }
716  else if (paramSpec->value_type == G_TYPE_DOUBLE)
717  {
718  double val;
719  rv = propertyVariant->GetAsDouble (&val);
720  NS_ENSURE_SUCCESS (rv, rv);
721  g_value_init (&propertyValue, G_TYPE_DOUBLE);
722  g_value_set_float (&propertyValue, val);
723  }
724  else if (paramSpec->value_type == G_TYPE_STRING)
725  {
726  nsCString val;
727  rv = propertyVariant->GetAsACString(val);
728  NS_ENSURE_SUCCESS (rv, rv);
729  g_value_init (&propertyValue, G_TYPE_STRING);
730  g_value_set_string (&propertyValue, val.BeginReading());
731  }
732  else if (G_TYPE_IS_ENUM (paramSpec->value_type))
733  {
734  PRUint32 val;
735  rv = propertyVariant->GetAsUint32 (&val);
736  NS_ENSURE_SUCCESS (rv, rv);
737  g_value_init (&propertyValue, paramSpec->value_type);
738  g_value_set_enum (&propertyValue, val);
739  }
740  else {
741  LOG(("Unsupported property type"));
742  return NS_ERROR_FAILURE;
743  }
744 
745  g_object_set_property (G_OBJECT (element),
746  propertyNameUTF8.BeginReading(),
747  &propertyValue);
748  g_value_unset (&propertyValue);
749  }
750 
751  return NS_OK;
752 }
753 
755  const char *mime_type;
756  const char *gst_name;
758 };
759 
760 static const struct sb_gst_caps_map_entry sb_gst_caps_map[] =
761 {
762  // mpeg audio we always want id3 tags on for the container
763  { "audio/mpeg", "application/x-id3", SB_GST_CAPS_MAP_CONTAINER },
764  // Quicktime video variants
765  { "video/3gpp", "video/quicktime", SB_GST_CAPS_MAP_CONTAINER },
766  { "video/mp4", "video/quicktime", SB_GST_CAPS_MAP_CONTAINER },
767 
768  { "audio/x-pcm-int", "audio/x-raw-int", SB_GST_CAPS_MAP_AUDIO },
769  { "audio/x-pcm-float", "audio/x-raw-float", SB_GST_CAPS_MAP_AUDIO },
770  { "audio/x-ms-wma", "audio/x-wma", SB_GST_CAPS_MAP_AUDIO },
771  { "audio/mpeg", "audio/mpeg", SB_GST_CAPS_MAP_AUDIO },
772  { "audio/aac", "audio/mpeg", SB_GST_CAPS_MAP_AUDIO },
773 
774  { "video/x-ms-wmv", "video/x-wmv", SB_GST_CAPS_MAP_VIDEO },
775  { "video/h264", "video/x-h264", SB_GST_CAPS_MAP_VIDEO },
776 
777  // The remaining ones have NONE type; they should ONLY be used for mapping
778  // a GST type to a mime type, not the other way around. It's ok for duplicate
779  // mime types (the first column) here, since they're not used for mapping
780  // mime types to caps.
781 
782  // MPEG4 video variants; treat them all as video/mpeg
783  { "video/mpeg", "video/x-divx", SB_GST_CAPS_MAP_NONE },
784  { "video/mpeg", "video/x-xvid", SB_GST_CAPS_MAP_NONE },
785 
786  // Quicktime file format variants, map them to the appropriate type
787  { "video/mp4", "audio/x-m4a", SB_GST_CAPS_MAP_NONE },
788  { "video/3gpp", "application/x-3gp", SB_GST_CAPS_MAP_NONE },
789 };
790 
791 /* GStreamer caps name are generally similar to mime-types, but some of them
792  * differ. We use this table to convert the ones that differ that we know about,
793  * and all other names are returned unchanged.
794  */
795 static nsCString
796 GetGstCapsName(const nsACString &aMimeType, enum sbGstCapsMapType aType)
797 {
798  nsCString result(aMimeType);
799 
800  for (unsigned int i = 0; i < NS_ARRAY_LENGTH (sb_gst_caps_map); i++) {
801  if (sb_gst_caps_map[i].map_type == aType &&
802  aMimeType.EqualsLiteral(sb_gst_caps_map[i].mime_type))
803  {
804  result.AssignLiteral(sb_gst_caps_map[i].gst_name);
805  return result;
806  }
807  }
808 
809  return result;
810 }
811 
812 GstCaps *
813 GetCapsForMimeType (const nsACString &aMimeType, enum sbGstCapsMapType aType )
814 {
815  nsCString name = GetGstCapsName (aMimeType, aType);
816  // set up a caps structure. Note that this ONLY provides the name part of the
817  // structure, it does NOT add attributes to the caps that may be required.
818  GstCaps *caps = gst_caps_from_string(name.BeginReading());
819  return caps;
820 }
821 
822 
823 nsresult
824 GetMimeTypeForCaps (GstCaps *aCaps, nsACString &aMimeType)
825 {
826  NS_ENSURE_ARG_POINTER(aCaps);
827  GstStructure *structure = gst_caps_get_structure (aCaps, 0);
828  NS_ENSURE_STATE(structure);
829  // ^ throws NS_ERROR_UNEXPECTED which is more appropriate for the situation
830  // than NS_ERROR_INVALID_POINTER of NS_ENSURE_ARG_POINTER
831  const gchar *capsName = gst_structure_get_name (structure);
832  NS_ENSURE_STATE(capsName);
833 
834  // Need to special-case some of these
835  if (!strcmp(capsName, "video/quicktime")) {
836  const gchar *variant = gst_structure_get_string (structure, "variant");
837  if (variant) {
838  if (!strcmp(variant, "3gpp"))
839  aMimeType.AssignLiteral("video/3gpp");
840  else if (!strcmp(variant, "iso"))
841  aMimeType.AssignLiteral("video/mp4");
842  else // variant is 'apple' or something we don't recognise; use quicktime.
843  aMimeType.AssignLiteral("video/quicktime");
844  }
845  else {
846  // No variant; use quicktime
847  aMimeType.AssignLiteral("video/quicktime");
848  }
849  return NS_OK;
850  }
851  else if (!strcmp(capsName, "audio/mpeg")) {
852  gint mpegversion;
853  if (gst_structure_get_int (structure, "mpegversion", &mpegversion) &&
854  mpegversion == 4) {
855  aMimeType.AssignLiteral("audio/aac");
856  }
857  else {
858  aMimeType.AssignLiteral("audio/mpeg");
859  }
860  return NS_OK;
861  }
862 
863  for (unsigned int i = 0; i < NS_ARRAY_LENGTH (sb_gst_caps_map); i++)
864  {
865  if (!strcmp(capsName, sb_gst_caps_map[i].gst_name)) {
866  aMimeType.AssignLiteral(sb_gst_caps_map[i].mime_type);
867  return NS_OK;
868  }
869  }
870 
871  /* Use the same name if we don't have a different mapping */
872  aMimeType.AssignLiteral(capsName);
873  return NS_OK;
874 
875 }
876 
877 
878 // A GstStructureForeachFunc. Adds each GValue to the nsIWritablePropertyBag2
879 // (in aUserData) using the property name given by aFieldId
880 static gboolean
881 _OnEachGValue(GQuark aFieldId, const GValue *aValue, gpointer aUserData)
882 {
883  const gchar * fieldname = g_quark_to_string(aFieldId);
884 
885  nsresult rv;
887  static_cast<nsIWritablePropertyBag2 *>(aUserData),
888  NS_ConvertASCIItoUTF16(fieldname),
889  aValue);
890  NS_ENSURE_SUCCESS(rv, FALSE);
891 
892  return TRUE;
893 }
894 
895 
896 nsresult
897 SetPropertiesFromGstStructure(nsIWritablePropertyBag2 * aPropertyBag,
898  const GstStructure * aStructure,
899  const gchar * const aDesiredFieldList[],
900  PRUint32 aFieldCount)
901 {
902  nsresult rv;
903 
904  // Use the list of field names if present. Otherwise, fall through
905  // and copy every field of aStructure to aPropertyBag
906  if (aDesiredFieldList) {
907  for (PRUint32 i = 0; i < aFieldCount; i++) {
908  const gchar * fieldname = aDesiredFieldList[i];
909  const GValue * value = gst_structure_get_value(aStructure, fieldname);
910 
911  // Silently skip missing fields
912  if (value) {
913  rv = SetPropertyFromGValue(aPropertyBag,
914  NS_ConvertASCIItoUTF16(fieldname),
915  value);
916  NS_ENSURE_SUCCESS(rv, rv);
917  }
918  }
919  return NS_OK;
920  }
921 
922  // If the field list is NULL, its count must be zero
923  NS_ENSURE_TRUE(aFieldCount == 0, NS_ERROR_INVALID_ARG);
924 
925  gboolean ok = gst_structure_foreach(aStructure, _OnEachGValue, aPropertyBag);
926  NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
927 
928  return NS_OK;
929 }
930 
931 
932 nsresult
933 SetPropertyFromGValue(nsIWritablePropertyBag2 * aPropertyBag,
934  const nsAString & aProperty,
935  const GValue * aValue)
936 {
937  NS_ENSURE_ARG_POINTER(aPropertyBag);
938 
939  nsresult rv;
940  GType type = G_VALUE_TYPE(aValue);
941  const gchar *asGstr = NULL;
942  gboolean asGboolean;
943  gint asGint;
944  guint asGuint;
945 
946  switch (type) {
947  case G_TYPE_BOOLEAN:
948  asGboolean = g_value_get_boolean (aValue);
949  rv = aPropertyBag->SetPropertyAsBool(aProperty, asGboolean);
950  NS_ENSURE_SUCCESS (rv, rv);
951  break;
952 
953  case G_TYPE_INT:
954  asGint = g_value_get_int (aValue);
955  rv = aPropertyBag->SetPropertyAsInt32(aProperty, asGint);
956  NS_ENSURE_SUCCESS (rv, rv);
957  break;
958 
959  case G_TYPE_UINT:
960  asGuint = g_value_get_uint (aValue);
961  rv = aPropertyBag->SetPropertyAsUint32(aProperty, asGuint);
962  NS_ENSURE_SUCCESS (rv, rv);
963  break;
964 
965  case G_TYPE_STRING:
966  asGstr = g_value_get_string (aValue);
967  NS_ENSURE_TRUE(asGstr, NS_ERROR_UNEXPECTED);
968  rv = aPropertyBag->SetPropertyAsACString(aProperty, nsCString(asGstr));
969  NS_ENSURE_SUCCESS (rv, rv);
970  break;
971 
972  default:
973  // Handle any types that have dynamic type identifiers. They can't be
974  // used as case labels because they aren't constant expressions
975 
976  if (type == GST_TYPE_BUFFER) {
977  const GstBuffer * asGstBuffer = gst_value_get_buffer(aValue);
978  NS_ENSURE_TRUE(asGstBuffer, NS_ERROR_UNEXPECTED);
979 
980  // Create a variant to hold the buffer data as an array of VTYPE_UINT8s
981  nsCOMPtr<nsIWritableVariant> asVariant =
982  do_CreateInstance("@mozilla.org/variant;1", &rv);
983  NS_ENSURE_SUCCESS (rv, rv);
984  rv = asVariant->SetAsArray(nsIDataType::VTYPE_UINT8,
985  nsnull,
986  GST_BUFFER_SIZE(asGstBuffer),
987  GST_BUFFER_DATA(asGstBuffer));
988  NS_ENSURE_SUCCESS (rv, rv);
989 
990  // QI to an nsIWritablePropertyBag to access the generic SetProperty()
991  // method
992  nsCOMPtr<nsIWritablePropertyBag> bagV1 =
993  do_QueryInterface(aPropertyBag, &rv);
994  NS_ENSURE_SUCCESS (rv, rv);
995  rv = bagV1->SetProperty(aProperty, asVariant);
996  NS_ENSURE_SUCCESS (rv, rv);
997  }
998  else {
999  // Unexpected data type
1000 #ifdef PR_LOGGING
1001  const gchar *typeName = g_type_name(type);
1002  LOG(("Unexpected GType [%u %s] for [%s]",
1003  unsigned(type),
1004  typeName,
1005  NS_LossyConvertUTF16toASCII(aProperty).get()));
1006 #endif // #ifdef PR_LOGGING
1007  NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_ILLEGAL_VALUE);
1008  }
1009  break;
1010  }
1011 
1012  return NS_OK;
1013 }
1014 
1015 GstPad *
1016 GetRealPad (GstPad *pad)
1017 {
1018  GstPad *current = pad;
1019  g_object_ref(current);
1020  GstGhostPad *ghost;
1021  while (GST_IS_GHOST_PAD (current)) {
1022  ghost = GST_GHOST_PAD (current);
1023  GstPad *padToRelease = current;
1024  current = gst_ghost_pad_get_target (ghost);
1025  g_object_unref (padToRelease);
1026  }
1027  return current;
1028 }
1029 
1030 
const char * mime_type
#define SB_PROPERTY_TOTALDISCS
return NS_OK
const unsigned long SB_RESOURCE_OPEN_READ
const unsigned long SB_STREAM_DECODE
static const struct errMap ResourceErrorMap[]
static nsCString GetGstCapsName(const nsACString &aMimeType, enum sbGstCapsMapType aType)
const unsigned long SB_RESOURCE_OPEN_WRITE
static const struct errMap StreamErrorMap[]
nsresult GetMediacoreErrorFromGstError(GError *gerror, nsString aResource, GStreamer::pipelineOp_t aPipelineOp, sbIMediacoreError **_retval)
#define SB_PROPERTY_ORIGINURL
void RegisterCustomTags()
_setDateDatepicker date
menuItem id
Definition: FeedWriter.js:971
onPageChanged aValue
Definition: FeedWriter.js:1395
GstPad * GetRealPad(GstPad *pad)
nsString Get(const nsAString &aKey, const nsAString &aDefault=SBVoidString())
nsString Format(const nsAString &aKey, nsTArray< nsString > &aParams, const nsAString &aDefault=SBVoidString())
var event
PRBool ConvertSinglePropertyToTag(sbIProperty *property, GstTagList *taglist)
#define SB_GST_TAG_GRACENOTE_TAGID
#define LOG(args)
const unsigned long SB_RESOURCE_NO_SPACE_LEFT
GstBusSyncReply SyncToAsyncDispatcher(GstBus *bus, GstMessage *message, gpointer data)
#define SB_PROPERTY_TOTALTRACKS
const unsigned long SB_STREAM_FAILURE
const unsigned long SB_STREAM_CODEC_NOT_FOUND
static gboolean _OnEachGValue(GQuark aFieldId, const GValue *aValue, gpointer aUserData)
#define SB_PROPERTY_BITRATE
GStreamer::pipelineOp_t gstPipelineOp
nsresult ApplyPropertyBagToElement(GstElement *element, nsIPropertyBag *props)
An interface to carry around arrays of nsIProperty instances Note that implementations of the interfa...
#define SB_GST_TAG_GRACENOTE_EXTENDED_DATA
GstCaps * GetCapsForMimeType(const nsACString &aMimeType, enum sbGstCapsMapType aType)
const unsigned long SB_STREAM_WRONG_TYPE
const char * sbErrorMessageName
var bundle
NS_IMPL_THREADSAFE_ISUPPORTS1(sbGstMessageEvent, nsIRunnable) GstBusSyncReply SyncToAsyncDispatcher(GstBus *bus
#define SB_PROPERTY_GENRE
GstTagList * ConvertPropertyArrayToTagList(sbIPropertyArray *properties)
return GST_BUS_DROP
static void ConvertSingleTag(const GstTagList *taglist, const gchar *tag, gpointer user_data)
nsresult SetPropertyFromGValue(nsIWritablePropertyBag2 *aPropertyBag, const nsAString &aProperty, const GValue *aValue)
#define PROPERTY_CONVERT_UINT(propname, tagname, scale)
#define TAG_CONVERT_STRING(sbname, gstname)
this _dialogInput val(dateText)
NS_DECL_ISUPPORTS sbGstMessageEvent(GstMessage *msg, sbGStreamerMessageHandler *handler)
#define SB_PROPERTY_DURATION
const unsigned long SB_RESOURCE_CLOSE
#define SB_PROPERTY_COPYRIGHT
#define SB_PROPERTY_DISCNUMBER
gst_message_unref(message)
const unsigned long SB_RESOURCE_OPEN_READ_WRITE
ExtensionSchemeMatcher prototype match
GstMessage * message
#define SB_PROPERTY_ARTISTNAME
enum sbGstCapsMapType map_type
static gboolean match_element_filter(GstPluginFeature *feature, TypeMatchingInfo *data)
Songbird String Bundle Definitions.
#define PROPERTY_CONVERT_STRING(propname, tagname)
const unsigned long SB_RESOURCE_SEEK
#define TAG_CONVERT_UINT(sbname, gstname)
const char * gst_name
StringArrayEnumerator prototype hasMore
countRef value
Definition: FeedWriter.js:1423
const unsigned long SB_STREAM_ENCODE
const unsigned long SB_RESOURCE_READ
#define SB_PROPERTY_ALBUMNAME
nsresult GetMimeTypeForCaps(GstCaps *aCaps, nsACString &aMimeType)
const unsigned long SB_STREAM_TYPE_NOT_FOUND
#define SB_PROPERTY_COMPOSERNAME
const unsigned long SB_RESOURCE_NOT_FOUND
nsresult ConvertTagListToPropertyArray(GstTagList *taglist, sbIPropertyArray **aPropertyArray)
#define SB_PROPERTY_YEAR
const unsigned long SB_RESOURCE_WRITE
#define SB_PROPERTY_TRACKNAME
nsresult SetPropertiesFromGstStructure(nsIWritablePropertyBag2 *aPropertyBag, const GstStructure *aStructure, const gchar *const aDesiredFieldList[], PRUint32 aFieldCount)
#define SB_PROPERTY_COMMENT
function msg
#define SB_GN_PROP_TAGID
dataSBGenres SBProperties tag
Definition: tuner2.js:871
#define SB_PROPERTY_COPYRIGHTURL
observe data
Definition: FeedWriter.js:1329
An interface to carry around arrays of nsIProperty instances. Users of this interface should only QI ...
#define SB_GN_PROP_EXTENDEDDATA
static const struct sb_gst_caps_map_entry sb_gst_caps_map[]
_getSelectedPageStyle s i
GstMessage gpointer data sbGStreamerMessageHandler * handler
const char * FindMatchingElementName(const char *srcCapsString, const char *typeName)
virtual PRBool HandleSynchronousMessage(GstMessage *message)=0
function next()
#define SB_PROPERTY_TRACKNUMBER