mozilla-gstreamer-760140.patch
author Wolfgang Rosenauer <wr@rosenauer.org>
Mon, 25 Mar 2013 23:57:09 +0100
changeset 637 73640b76d6c3
parent 612 3006d73ad2fa
permissions -rw-r--r--
first building version of FF21

# HG changeset patch
# Parent 74ba8ebd0dc72be84280bd4806f84d9ec1f4e130
Bug 760140 - Query the GstRegistry for the required demuxers/decoders from canPlayType (TM: 22)

diff --git a/content/media/DecoderTraits.cpp b/content/media/DecoderTraits.cpp
--- a/content/media/DecoderTraits.cpp
+++ b/content/media/DecoderTraits.cpp
@@ -7,16 +7,18 @@
 #include "DecoderTraits.h"
 #include "MediaDecoder.h"
 #include "nsCharSeparatedTokenizer.h"
 #ifdef MOZ_MEDIA_PLUGINS
 #include "MediaPluginHost.h"
 #endif
 #ifdef MOZ_GSTREAMER
 #include "mozilla/Preferences.h"
+#include "GStreamerDecoder.h"
+#include "nsXPCOMStrings.h"
 #endif
 #ifdef MOZ_WMF
 #include "WMFDecoder.h"
 #endif
 
 namespace mozilla
 {
 
@@ -137,48 +139,35 @@ DecoderTraits::IsWebMType(const nsACStri
     return false;
   }
 
   return CodecListContains(gWebMTypes, aType);
 }
 #endif
 
 #ifdef MOZ_GSTREAMER
-static const char* const gH264Types[4] = {
-  "video/mp4",
-  "video/3gpp",
-  "video/quicktime",
-  nullptr
-};
-
 bool
 DecoderTraits::IsGStreamerSupportedType(const nsACString& aMimeType)
 {
   if (!MediaDecoder::IsGStreamerEnabled())
     return false;
-  if (IsH264Type(aMimeType))
+  if (GStreamerDecoder::CanHandleMediaType(aMimeType, nullptr))
     return true;
   if (!Preferences::GetBool("media.prefer-gstreamer", false))
     return false;
 #ifdef MOZ_WEBM
   if (IsWebMType(aMimeType))
     return true;
 #endif
 #ifdef MOZ_OGG
   if (IsOggType(aMimeType))
     return true;
 #endif
   return false;
 }
-
-bool
-DecoderTraits::IsH264Type(const nsACString& aType)
-{
-  return CodecListContains(gH264Types, aType);
-}
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 static const char* const gOmxTypes[6] = {
   "audio/mpeg",
   "audio/mp4",
   "video/mp4",
   "video/3gpp",
@@ -190,19 +179,17 @@ bool
 DecoderTraits::IsOmxSupportedType(const nsACString& aType)
 {
   if (!MediaDecoder::IsOmxEnabled()) {
     return false;
   }
 
   return CodecListContains(gOmxTypes, aType);
 }
-#endif
 
-#if defined(MOZ_GSTREAMER) || defined(MOZ_WIDGET_GONK)
 static char const *const gH264Codecs[9] = {
   "avc1.42E01E",  // H.264 Constrained Baseline Profile Level 3.0
   "avc1.42001E",  // H.264 Baseline Profile Level 3.0
   "avc1.58A01E",  // H.264 Extended Profile Level 3.0
   "avc1.4D401E",  // H.264 Main Profile Level 3.0
   "avc1.64001E",  // H.264 High Profile Level 3.0
   "avc1.64001F",  // H.264 High Profile Level 3.1
   "mp4v.20.3",    // 3GPP
@@ -303,19 +290,19 @@ DecoderTraits::CanHandleMediaType(const 
 #ifdef MOZ_DASH
   if (IsDASHMPDType(nsDependentCString(aMIMEType))) {
     // DASH manifest uses WebM codecs only.
     codecList = gWebMCodecs;
     result = CANPLAY_YES;
   }
 #endif
 #ifdef MOZ_GSTREAMER
-  if (IsH264Type(nsDependentCString(aMIMEType))) {
-    codecList = gH264Codecs;
-    result = CANPLAY_MAYBE;
+  if (GStreamerDecoder::CanHandleMediaType(nsDependentCString(aMIMEType),
+                                           aHaveRequestedCodecs ? &aRequestedCodecs : nullptr)) {
+    return CANPLAY_YES;
   }
 #endif
 #ifdef MOZ_WIDGET_GONK
   if (IsOmxSupportedType(nsDependentCString(aMIMEType))) {
     codecList = gH264Codecs;
     result = CANPLAY_MAYBE;
   }
 #endif
@@ -324,17 +311,17 @@ DecoderTraits::CanHandleMediaType(const 
     result = CANPLAY_MAYBE;
   }
 #endif
 #ifdef MOZ_MEDIA_PLUGINS
   if (MediaDecoder::IsMediaPluginsEnabled() &&
       GetMediaPluginHost()->FindDecoder(nsDependentCString(aMIMEType), &codecList))
     result = CANPLAY_MAYBE;
 #endif
-  if (result == CANPLAY_NO || !aHaveRequestedCodecs) {
+  if (result == CANPLAY_NO || !aHaveRequestedCodecs || !codecList) {
     return result;
   }
 
   // See http://www.rfc-editor.org/rfc/rfc4281.txt for the description
   // of the 'codecs' parameter
   nsCharSeparatedTokenizer tokenizer(aRequestedCodecs, ',');
   bool expectMoreTokens = false;
   while (tokenizer.hasMoreTokens()) {
diff --git a/content/media/DecoderTraits.h b/content/media/DecoderTraits.h
--- a/content/media/DecoderTraits.h
+++ b/content/media/DecoderTraits.h
@@ -51,17 +51,16 @@ public:
 #ifdef MOZ_WEBM
   static bool IsWebMType(const nsACString& aType);
 #endif
 
 #ifdef MOZ_GSTREAMER
   // When enabled, use GStreamer for H.264, but not for codecs handled by our
   // bundled decoders, unless the "media.prefer-gstreamer" pref is set.
   static bool IsGStreamerSupportedType(const nsACString& aType);
-  static bool IsH264Type(const nsACString& aType);
 #endif
 
 #ifdef MOZ_WIDGET_GONK
   static bool IsOmxSupportedType(const nsACString& aType);
 #endif
 
 #ifdef MOZ_MEDIA_PLUGINS
   static bool IsMediaPluginsType(const nsACString& aType);
diff --git a/content/media/gstreamer/GStreamerDecoder.cpp b/content/media/gstreamer/GStreamerDecoder.cpp
--- a/content/media/gstreamer/GStreamerDecoder.cpp
+++ b/content/media/gstreamer/GStreamerDecoder.cpp
@@ -2,18 +2,26 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaDecoderStateMachine.h"
 #include "GStreamerReader.h"
 #include "GStreamerDecoder.h"
+#include "GStreamerFormatHelper.h"
 
 namespace mozilla {
 
 MediaDecoderStateMachine* GStreamerDecoder::CreateStateMachine()
 {
   return new MediaDecoderStateMachine(this, new GStreamerReader(this));
 }
 
+bool
+GStreamerDecoder::CanHandleMediaType(const nsACString& aMIMEType,
+                                     const nsAString* aCodecs)
+{
+  return GStreamerFormatHelper::Instance()->CanHandleMediaType(aMIMEType, aCodecs);
+}
+
 } // namespace mozilla
 
diff --git a/content/media/gstreamer/GStreamerDecoder.h b/content/media/gstreamer/GStreamerDecoder.h
--- a/content/media/gstreamer/GStreamerDecoder.h
+++ b/content/media/gstreamer/GStreamerDecoder.h
@@ -3,21 +3,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #if !defined(GStreamerDecoder_h_)
 #define GStreamerDecoder_h_
 
 #include "MediaDecoder.h"
+#include "nsXPCOMStrings.h"
 
 namespace mozilla {
 
 class GStreamerDecoder : public MediaDecoder
 {
 public:
   virtual MediaDecoder* Clone() { return new GStreamerDecoder(); }
   virtual MediaDecoderStateMachine* CreateStateMachine();
+  static bool CanHandleMediaType(const nsACString& aMIMEType, const nsAString* aCodecs);
 };
 
 } // namespace mozilla
 
 #endif
diff --git a/content/media/gstreamer/GStreamerFormatHelper.cpp b/content/media/gstreamer/GStreamerFormatHelper.cpp
new file mode 100644
--- /dev/null
+++ b/content/media/gstreamer/GStreamerFormatHelper.cpp
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GStreamerFormatHelper.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsXPCOMStrings.h"
+
+#define ENTRY_FORMAT(entry) entry[0]
+#define ENTRY_CAPS(entry) entry[1]
+
+GStreamerFormatHelper* GStreamerFormatHelper::gInstance = nullptr;
+
+GStreamerFormatHelper* GStreamerFormatHelper::Instance() {
+  if (!gInstance) {
+    gst_init(nullptr, nullptr);
+    gInstance = new GStreamerFormatHelper();
+  }
+
+  return gInstance;
+}
+
+void GStreamerFormatHelper::Shutdown() {
+  if (gInstance) {
+    delete gInstance;
+    gInstance = nullptr;
+  }
+}
+
+char const *const GStreamerFormatHelper::mContainers[4][2] = {
+  {"video/mp4", "video/quicktime"},
+  {"video/quicktime", "video/quicktime"},
+  {"audio/mp4", "audio/mpeg, mpegversion=(int)4"},
+  {"audio/mpeg", "audio/mpeg, mpegversion=(int)1"},
+};
+
+char const *const GStreamerFormatHelper::mCodecs[8][2] = {
+  {"avc1.42E01E", "video/x-h264"},
+  {"avc1.42001E", "video/x-h264"},
+  {"avc1.58A01E", "video/x-h264"},
+  {"avc1.4D401E", "video/x-h264"},
+  {"avc1.64001E", "video/x-h264"},
+  {"avc1.64001F", "video/x-h264"},
+  {"mp4v.20.3", "video/3gpp"},
+  {"mp4a.40.2", "audio/mpeg, mpegversion=(int)4"},
+};
+
+GStreamerFormatHelper::GStreamerFormatHelper()
+  : mFactories(nullptr),
+    mCookie(static_cast<uint32_t>(-1))
+{
+}
+
+GStreamerFormatHelper::~GStreamerFormatHelper() {
+  if (mFactories)
+    g_list_free(mFactories);
+}
+
+bool GStreamerFormatHelper::CanHandleMediaType(const nsACString& aMIMEType,
+                                               const nsAString* aCodecs) {
+  const char *type;
+  NS_CStringGetData(aMIMEType, &type, NULL);
+
+  GstCaps* caps = ConvertFormatsToCaps(type, aCodecs);
+  if (!caps) {
+    return false;
+  }
+
+  bool ret = HaveElementsToProcessCaps(caps);
+  gst_caps_unref(caps);
+
+  return ret;
+}
+
+GstCaps* GStreamerFormatHelper::ConvertFormatsToCaps(const char* aMIMEType,
+                                                     const nsAString* aCodecs) {
+  unsigned int i;
+
+  /* convert aMIMEType to gst container caps */
+  const char* capsString = nullptr;
+  for (i = 0; i < G_N_ELEMENTS(mContainers); i++) {
+    if (!strcmp(ENTRY_FORMAT(mContainers[i]), aMIMEType)) {
+      capsString = ENTRY_CAPS(mContainers[i]);
+      break;
+    }
+  }
+
+  if (!capsString) {
+    /* we couldn't find any matching caps */
+    return nullptr;
+  }
+
+  GstCaps* caps = gst_caps_from_string(capsString);
+  /* container only */
+  if (!aCodecs) {
+    return caps;
+  }
+
+  nsCharSeparatedTokenizer tokenizer(*aCodecs, ',');
+  while (tokenizer.hasMoreTokens()) {
+    const nsSubstring& codec = tokenizer.nextToken();
+    capsString = nullptr;
+
+    for (i = 0; i < G_N_ELEMENTS(mCodecs); i++) {
+      if (codec.EqualsASCII(ENTRY_FORMAT(mCodecs[i]))) {
+        capsString = ENTRY_CAPS(mCodecs[i]);
+        break;
+      }
+    }
+
+    if (!capsString) {
+      gst_caps_unref(caps);
+      return nullptr;
+    }
+
+    GstCaps* tmp = gst_caps_from_string(capsString);
+    /* appends and frees tmp */
+    gst_caps_append(caps, tmp);
+  }
+
+  return caps;
+}
+
+bool GStreamerFormatHelper::HaveElementsToProcessCaps(GstCaps* aCaps) {
+
+  GList* factories = GetFactories();
+
+  GList* list;
+  /* here aCaps contains [containerCaps, [codecCaps1, [codecCaps2, ...]]] so process
+   * caps structures individually as we want one element for _each_
+   * structure */
+  for (unsigned int i = 0; i < gst_caps_get_size(aCaps); i++) {
+    GstStructure* s = gst_caps_get_structure(aCaps, i);
+    GstCaps* caps = gst_caps_new_full(gst_structure_copy(s), nullptr);
+    list = gst_element_factory_list_filter (factories, caps, GST_PAD_SINK, FALSE);
+    gst_caps_unref(caps);
+    if (!list) {
+      return false;
+    }
+    g_list_free(list);
+  }
+
+  return true;
+}
+
+GList* GStreamerFormatHelper::GetFactories() {
+  uint32_t cookie = gst_default_registry_get_feature_list_cookie ();
+  if (cookie != mCookie) {
+    g_list_free(mFactories);
+    mFactories = gst_element_factory_list_get_elements
+        (GST_ELEMENT_FACTORY_TYPE_DEMUXER | GST_ELEMENT_FACTORY_TYPE_DECODER,
+         GST_RANK_MARGINAL);
+    mCookie = cookie;
+  }
+
+  return mFactories;
+}
diff --git a/content/media/gstreamer/GStreamerFormatHelper.h b/content/media/gstreamer/GStreamerFormatHelper.h
new file mode 100644
--- /dev/null
+++ b/content/media/gstreamer/GStreamerFormatHelper.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(GStreamerFormatHelper_h_)
+#define GStreamerFormatHelper_h_
+
+#include <gst/gst.h>
+#include <mozilla/Types.h>
+#include "nsXPCOMStrings.h"
+
+class GStreamerFormatHelper {
+  /* This class can be used to query the GStreamer registry for the required
+   * demuxers/decoders from nsHTMLMediaElement::CanPlayType.
+   * It implements looking at the GstRegistry to check if elements to
+   * demux/decode the formats passed to CanPlayType() are actually installed.
+   */
+  public:
+    static GStreamerFormatHelper* Instance();
+    ~GStreamerFormatHelper();
+
+    bool CanHandleMediaType(const nsACString& aMIMEType,
+                            const nsAString* aCodecs);
+
+   static void Shutdown();
+
+  private:
+    GStreamerFormatHelper();
+    GstCaps* ConvertFormatsToCaps(const char* aMIMEType,
+                                  const nsAString* aCodecs);
+    char* const *CodecListFromCaps(GstCaps* aCaps);
+    bool HaveElementsToProcessCaps(GstCaps* aCaps);
+    GList* GetFactories();
+
+    static GStreamerFormatHelper* gInstance;
+
+    /* table to convert from container MIME types to GStreamer elements */
+    static char const *const mContainers[4][2];
+
+    /* table to convert from codec MIME types to GStreamer elements */
+    static char const *const mCodecs[8][2];
+
+    /* list of GStreamer element factories
+     * Element factories are the basic types retrieved from the GStreamer
+     * registry, they describe all plugins and elements that GStreamer can
+     * create.
+     * This means that element factories are useful for automated element
+     * instancing, such as what autopluggers do,
+     * and for creating lists of available elements. */
+    GList* mFactories;
+
+    /* Storage for the default registrys feature list cookie.
+     * It changes every time a feature is added to or removed from the
+     * GStreamer registry. */
+    uint32_t mCookie;
+};
+
+#endif
diff --git a/content/media/gstreamer/Makefile.in b/content/media/gstreamer/Makefile.in
--- a/content/media/gstreamer/Makefile.in
+++ b/content/media/gstreamer/Makefile.in
@@ -13,21 +13,23 @@ include $(DEPTH)/config/autoconf.mk
 MODULE		= content
 LIBRARY_NAME	= gkcongstreamer_s
 LIBXUL_LIBRARY 	= 1
 
 
 EXPORTS		+= \
 		GStreamerDecoder.h \
 		GStreamerReader.h \
+		GStreamerFormatHelper.h \
 		$(NULL)
 
 CPPSRCS		= \
 		GStreamerReader.cpp \
 		GStreamerDecoder.cpp \
+		GStreamerFormatHelper.cpp \
 		$(NULL)
 
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 CFLAGS		+= $(GSTREAMER_CFLAGS)
 CXXFLAGS	+= $(GSTREAMER_CFLAGS)
diff --git a/layout/build/Makefile.in b/layout/build/Makefile.in
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -318,16 +318,20 @@ LOCAL_INCLUDES	+= -I$(srcdir)/../base \
 		   -I$(topsrcdir)/js/xpconnect/loader \
 		   -I$(topsrcdir)/caps/include \
 		   -I$(topsrcdir)/netwerk/base/src \
 		   -I$(topsrcdir)/content/svg/content/src \
 		   -I$(topsrcdir)/extensions/cookie \
 		   -I$(topsrcdir)/netwerk/cookie \
 		   $(NULL)
 
+ifdef MOZ_GSTREAMER
+LOCAL_INCLUDES	+= $(GSTREAMER_CFLAGS)
+endif
+
 ifdef MOZ_B2G_RIL #{
 LOCAL_INCLUDES	+= -I$(topsrcdir)/dom/system/gonk
 endif #}
 
 ifdef MOZ_B2G_FM #{
 LOCAL_INCLUDES	+= -I$(topsrcdir)/dom/fm
 endif #}
 
diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -79,16 +79,20 @@
 #ifdef MOZ_MEDIA_PLUGINS
 #include "MediaPluginHost.h"
 #endif
 
 #ifdef MOZ_WMF
 #include "WMFDecoder.h"
 #endif
 
+#ifdef MOZ_GSTREAMER
+#include "GStreamerFormatHelper.h"
+#endif
+
 #ifdef MOZ_SYDNEYAUDIO
 #include "AudioStream.h"
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 #include "nsVolumeService.h"
 using namespace mozilla::system;
 #endif
@@ -344,16 +348,20 @@ nsLayoutStatics::Shutdown()
   nsXBLService::Shutdown();
   nsAutoCopyListener::Shutdown();
   FrameLayerBuilder::Shutdown();
 
 #ifdef MOZ_MEDIA_PLUGINS
   MediaPluginHost::Shutdown();
 #endif
 
+#ifdef MOZ_GSTREAMER
+  GStreamerFormatHelper::Shutdown();
+#endif
+
 #ifdef MOZ_SYDNEYAUDIO
   AudioStream::ShutdownLibrary();
 #endif
 
 #ifdef MOZ_WMF
   WMFDecoder::UnloadDLLs();
 #endif