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