content/media/gstreamer/GStreamerFormatHelper.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "GStreamerFormatHelper.h"
michael@0 8 #include "nsCharSeparatedTokenizer.h"
michael@0 9 #include "nsString.h"
michael@0 10 #include "GStreamerLoader.h"
michael@0 11
michael@0 12 #define ENTRY_FORMAT(entry) entry[0]
michael@0 13 #define ENTRY_CAPS(entry) entry[1]
michael@0 14
michael@0 15 namespace mozilla {
michael@0 16
michael@0 17 GStreamerFormatHelper* GStreamerFormatHelper::gInstance = nullptr;
michael@0 18 bool GStreamerFormatHelper::sLoadOK = false;
michael@0 19
michael@0 20 GStreamerFormatHelper* GStreamerFormatHelper::Instance() {
michael@0 21 if (!gInstance) {
michael@0 22 if ((sLoadOK = load_gstreamer())) {
michael@0 23 gst_init(nullptr, nullptr);
michael@0 24 }
michael@0 25
michael@0 26 gInstance = new GStreamerFormatHelper();
michael@0 27 }
michael@0 28
michael@0 29 return gInstance;
michael@0 30 }
michael@0 31
michael@0 32 void GStreamerFormatHelper::Shutdown() {
michael@0 33 delete gInstance;
michael@0 34 gInstance = nullptr;
michael@0 35 }
michael@0 36
michael@0 37 static char const *const sContainers[6][2] = {
michael@0 38 {"video/mp4", "video/quicktime"},
michael@0 39 {"video/quicktime", "video/quicktime"},
michael@0 40 {"audio/mp4", "audio/x-m4a"},
michael@0 41 {"audio/x-m4a", "audio/x-m4a"},
michael@0 42 {"audio/mpeg", "audio/mpeg, mpegversion=(int)1"},
michael@0 43 {"audio/mp3", "audio/mpeg, mpegversion=(int)1"},
michael@0 44 };
michael@0 45
michael@0 46 static char const *const sCodecs[9][2] = {
michael@0 47 {"avc1.42E01E", "video/x-h264"},
michael@0 48 {"avc1.42001E", "video/x-h264"},
michael@0 49 {"avc1.58A01E", "video/x-h264"},
michael@0 50 {"avc1.4D401E", "video/x-h264"},
michael@0 51 {"avc1.64001E", "video/x-h264"},
michael@0 52 {"avc1.64001F", "video/x-h264"},
michael@0 53 {"mp4v.20.3", "video/3gpp"},
michael@0 54 {"mp4a.40.2", "audio/mpeg, mpegversion=(int)4"},
michael@0 55 {"mp3", "audio/mpeg, mpegversion=(int)1"},
michael@0 56 };
michael@0 57
michael@0 58 static char const * const sDefaultCodecCaps[][2] = {
michael@0 59 {"video/mp4", "video/x-h264"},
michael@0 60 {"video/quicktime", "video/x-h264"},
michael@0 61 {"audio/mp4", "audio/mpeg, mpegversion=(int)4"},
michael@0 62 {"audio/x-m4a", "audio/mpeg, mpegversion=(int)4"},
michael@0 63 {"audio/mp3", "audio/mpeg, layer=(int)3"},
michael@0 64 {"audio/mpeg", "audio/mpeg, layer=(int)3"}
michael@0 65 };
michael@0 66
michael@0 67 GStreamerFormatHelper::GStreamerFormatHelper()
michael@0 68 : mFactories(nullptr),
michael@0 69 mCookie(static_cast<uint32_t>(-1))
michael@0 70 {
michael@0 71 if (!sLoadOK) {
michael@0 72 return;
michael@0 73 }
michael@0 74
michael@0 75 mSupportedContainerCaps = gst_caps_new_empty();
michael@0 76 for (unsigned int i = 0; i < G_N_ELEMENTS(sContainers); i++) {
michael@0 77 const char* capsString = sContainers[i][1];
michael@0 78 GstCaps* caps = gst_caps_from_string(capsString);
michael@0 79 gst_caps_append(mSupportedContainerCaps, caps);
michael@0 80 }
michael@0 81
michael@0 82 mSupportedCodecCaps = gst_caps_new_empty();
michael@0 83 for (unsigned int i = 0; i < G_N_ELEMENTS(sCodecs); i++) {
michael@0 84 const char* capsString = sCodecs[i][1];
michael@0 85 GstCaps* caps = gst_caps_from_string(capsString);
michael@0 86 gst_caps_append(mSupportedCodecCaps, caps);
michael@0 87 }
michael@0 88 }
michael@0 89
michael@0 90 GStreamerFormatHelper::~GStreamerFormatHelper() {
michael@0 91 if (!sLoadOK) {
michael@0 92 return;
michael@0 93 }
michael@0 94
michael@0 95 gst_caps_unref(mSupportedContainerCaps);
michael@0 96 gst_caps_unref(mSupportedCodecCaps);
michael@0 97
michael@0 98 if (mFactories)
michael@0 99 g_list_free(mFactories);
michael@0 100 }
michael@0 101
michael@0 102 static GstCaps *
michael@0 103 GetContainerCapsFromMIMEType(const char *aType) {
michael@0 104 /* convert aMIMEType to gst container caps */
michael@0 105 const char* capsString = nullptr;
michael@0 106 for (uint32_t i = 0; i < G_N_ELEMENTS(sContainers); i++) {
michael@0 107 if (!strcmp(ENTRY_FORMAT(sContainers[i]), aType)) {
michael@0 108 capsString = ENTRY_CAPS(sContainers[i]);
michael@0 109 break;
michael@0 110 }
michael@0 111 }
michael@0 112
michael@0 113 if (!capsString) {
michael@0 114 /* we couldn't find any matching caps */
michael@0 115 return nullptr;
michael@0 116 }
michael@0 117
michael@0 118 return gst_caps_from_string(capsString);
michael@0 119 }
michael@0 120
michael@0 121 static GstCaps *
michael@0 122 GetDefaultCapsFromMIMEType(const char *aType) {
michael@0 123 GstCaps *caps = GetContainerCapsFromMIMEType(aType);
michael@0 124
michael@0 125 for (uint32_t i = 0; i < G_N_ELEMENTS(sDefaultCodecCaps); i++) {
michael@0 126 if (!strcmp(sDefaultCodecCaps[i][0], aType)) {
michael@0 127 GstCaps *tmp = gst_caps_from_string(sDefaultCodecCaps[i][1]);
michael@0 128
michael@0 129 gst_caps_append(caps, tmp);
michael@0 130 return caps;
michael@0 131 }
michael@0 132 }
michael@0 133
michael@0 134 return nullptr;
michael@0 135 }
michael@0 136
michael@0 137 bool GStreamerFormatHelper::CanHandleMediaType(const nsACString& aMIMEType,
michael@0 138 const nsAString* aCodecs) {
michael@0 139 if (!sLoadOK) {
michael@0 140 return false;
michael@0 141 }
michael@0 142
michael@0 143 const char *type;
michael@0 144 NS_CStringGetData(aMIMEType, &type, nullptr);
michael@0 145
michael@0 146 GstCaps *caps;
michael@0 147 if (aCodecs && !aCodecs->IsEmpty()) {
michael@0 148 caps = ConvertFormatsToCaps(type, aCodecs);
michael@0 149 } else {
michael@0 150 // Get a minimal set of codec caps for this MIME type we should support so
michael@0 151 // that we don't overreport MIME types we are able to play.
michael@0 152 caps = GetDefaultCapsFromMIMEType(type);
michael@0 153 }
michael@0 154
michael@0 155 if (!caps) {
michael@0 156 return false;
michael@0 157 }
michael@0 158
michael@0 159 bool ret = HaveElementsToProcessCaps(caps);
michael@0 160 gst_caps_unref(caps);
michael@0 161
michael@0 162 return ret;
michael@0 163 }
michael@0 164
michael@0 165 GstCaps* GStreamerFormatHelper::ConvertFormatsToCaps(const char* aMIMEType,
michael@0 166 const nsAString* aCodecs) {
michael@0 167 NS_ASSERTION(sLoadOK, "GStreamer library not linked");
michael@0 168
michael@0 169 unsigned int i;
michael@0 170
michael@0 171 GstCaps *caps = GetContainerCapsFromMIMEType(aMIMEType);
michael@0 172 if (!caps) {
michael@0 173 return nullptr;
michael@0 174 }
michael@0 175
michael@0 176 /* container only */
michael@0 177 if (!aCodecs) {
michael@0 178 return caps;
michael@0 179 }
michael@0 180
michael@0 181 nsCharSeparatedTokenizer tokenizer(*aCodecs, ',');
michael@0 182 while (tokenizer.hasMoreTokens()) {
michael@0 183 const nsSubstring& codec = tokenizer.nextToken();
michael@0 184 const char *capsString = nullptr;
michael@0 185
michael@0 186 for (i = 0; i < G_N_ELEMENTS(sCodecs); i++) {
michael@0 187 if (codec.EqualsASCII(ENTRY_FORMAT(sCodecs[i]))) {
michael@0 188 capsString = ENTRY_CAPS(sCodecs[i]);
michael@0 189 break;
michael@0 190 }
michael@0 191 }
michael@0 192
michael@0 193 if (!capsString) {
michael@0 194 gst_caps_unref(caps);
michael@0 195 return nullptr;
michael@0 196 }
michael@0 197
michael@0 198 GstCaps* tmp = gst_caps_from_string(capsString);
michael@0 199 /* appends and frees tmp */
michael@0 200 gst_caps_append(caps, tmp);
michael@0 201 }
michael@0 202
michael@0 203 return caps;
michael@0 204 }
michael@0 205
michael@0 206 static gboolean FactoryFilter(GstPluginFeature *aFeature, gpointer)
michael@0 207 {
michael@0 208 if (!GST_IS_ELEMENT_FACTORY(aFeature)) {
michael@0 209 return FALSE;
michael@0 210 }
michael@0 211
michael@0 212 // TODO _get_klass doesn't exist in 1.0
michael@0 213 const gchar *className =
michael@0 214 gst_element_factory_get_klass(GST_ELEMENT_FACTORY_CAST(aFeature));
michael@0 215
michael@0 216 if (!strstr(className, "Decoder") && !strstr(className, "Demux")) {
michael@0 217 return FALSE;
michael@0 218 }
michael@0 219
michael@0 220 return gst_plugin_feature_get_rank(aFeature) >= GST_RANK_MARGINAL;
michael@0 221 }
michael@0 222
michael@0 223 /**
michael@0 224 * Returns true if any |aFactory| caps intersect with |aCaps|
michael@0 225 */
michael@0 226 static bool SupportsCaps(GstElementFactory *aFactory, GstCaps *aCaps)
michael@0 227 {
michael@0 228 for (const GList *iter = gst_element_factory_get_static_pad_templates(aFactory); iter; iter = iter->next) {
michael@0 229 GstStaticPadTemplate *templ = static_cast<GstStaticPadTemplate *>(iter->data);
michael@0 230
michael@0 231 if (templ->direction == GST_PAD_SRC) {
michael@0 232 continue;
michael@0 233 }
michael@0 234
michael@0 235 GstCaps *caps = gst_static_caps_get(&templ->static_caps);
michael@0 236 if (!caps) {
michael@0 237 continue;
michael@0 238 }
michael@0 239
michael@0 240 if (gst_caps_can_intersect(gst_static_caps_get(&templ->static_caps), aCaps)) {
michael@0 241 return true;
michael@0 242 }
michael@0 243 }
michael@0 244
michael@0 245 return false;
michael@0 246 }
michael@0 247
michael@0 248 bool GStreamerFormatHelper::HaveElementsToProcessCaps(GstCaps* aCaps) {
michael@0 249 NS_ASSERTION(sLoadOK, "GStreamer library not linked");
michael@0 250
michael@0 251 GList* factories = GetFactories();
michael@0 252
michael@0 253 /* here aCaps contains [containerCaps, [codecCaps1, [codecCaps2, ...]]] so process
michael@0 254 * caps structures individually as we want one element for _each_
michael@0 255 * structure */
michael@0 256 for (unsigned int i = 0; i < gst_caps_get_size(aCaps); i++) {
michael@0 257 GstStructure* s = gst_caps_get_structure(aCaps, i);
michael@0 258 GstCaps* caps = gst_caps_new_full(gst_structure_copy(s), nullptr);
michael@0 259
michael@0 260 bool found = false;
michael@0 261 for (GList *elem = factories; elem; elem = elem->next) {
michael@0 262 if (SupportsCaps(GST_ELEMENT_FACTORY_CAST(elem->data), caps)) {
michael@0 263 found = true;
michael@0 264 break;
michael@0 265 }
michael@0 266 }
michael@0 267
michael@0 268 if (!found) {
michael@0 269 return false;
michael@0 270 }
michael@0 271
michael@0 272 gst_caps_unref(caps);
michael@0 273 }
michael@0 274
michael@0 275 return true;
michael@0 276 }
michael@0 277
michael@0 278 bool GStreamerFormatHelper::CanHandleContainerCaps(GstCaps* aCaps)
michael@0 279 {
michael@0 280 NS_ASSERTION(sLoadOK, "GStreamer library not linked");
michael@0 281
michael@0 282 return gst_caps_can_intersect(aCaps, mSupportedContainerCaps);
michael@0 283 }
michael@0 284
michael@0 285 bool GStreamerFormatHelper::CanHandleCodecCaps(GstCaps* aCaps)
michael@0 286 {
michael@0 287 NS_ASSERTION(sLoadOK, "GStreamer library not linked");
michael@0 288
michael@0 289 return gst_caps_can_intersect(aCaps, mSupportedCodecCaps);
michael@0 290 }
michael@0 291
michael@0 292 GList* GStreamerFormatHelper::GetFactories() {
michael@0 293 NS_ASSERTION(sLoadOK, "GStreamer library not linked");
michael@0 294
michael@0 295 #if GST_VERSION_MAJOR >= 1
michael@0 296 uint32_t cookie = gst_registry_get_feature_list_cookie(gst_registry_get());
michael@0 297 #else
michael@0 298 uint32_t cookie = gst_default_registry_get_feature_list_cookie();
michael@0 299 #endif
michael@0 300 if (cookie != mCookie) {
michael@0 301 g_list_free(mFactories);
michael@0 302 #if GST_VERSION_MAJOR >= 1
michael@0 303 mFactories =
michael@0 304 gst_registry_feature_filter(gst_registry_get(),
michael@0 305 (GstPluginFeatureFilter)FactoryFilter,
michael@0 306 false, nullptr);
michael@0 307 #else
michael@0 308 mFactories =
michael@0 309 gst_default_registry_feature_filter((GstPluginFeatureFilter)FactoryFilter,
michael@0 310 false, nullptr);
michael@0 311 #endif
michael@0 312 mCookie = cookie;
michael@0 313 }
michael@0 314
michael@0 315 return mFactories;
michael@0 316 }
michael@0 317
michael@0 318 } // namespace mozilla

mercurial