1/*
2 * Copyright (C) 2009, 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
3 * Copyright (C) 2013 Collabora Ltd.
4 * Copyright (C) 2019 Igalia S.L.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include "config.h"
22#include "WebKitWebSourceGStreamer.h"
23
24#if ENABLE(VIDEO) && USE(GSTREAMER)
25
26#include "GStreamerCommon.h"
27#include "HTTPHeaderNames.h"
28#include "MainThreadNotifier.h"
29#include "MediaPlayer.h"
30#include "PlatformMediaResourceLoader.h"
31#include "ResourceError.h"
32#include "ResourceRequest.h"
33#include "ResourceResponse.h"
34#include <cstdint>
35#include <wtf/Condition.h>
36#include <wtf/Scope.h>
37#include <wtf/text/CString.h>
38
39using namespace WebCore;
40
41class CachedResourceStreamingClient final : public PlatformMediaResourceClient {
42 WTF_MAKE_NONCOPYABLE(CachedResourceStreamingClient);
43public:
44 CachedResourceStreamingClient(WebKitWebSrc*, ResourceRequest&&);
45 virtual ~CachedResourceStreamingClient();
46
47 const HashSet<RefPtr<WebCore::SecurityOrigin>>& securityOrigins() const { return m_origins; }
48
49 void setSourceElement(WebKitWebSrc* src) { m_src = GST_ELEMENT_CAST(src); }
50
51private:
52 void checkUpdateBlocksize(uint64_t bytesRead);
53
54 // PlatformMediaResourceClient virtual methods.
55 void responseReceived(PlatformMediaResource&, const ResourceResponse&, CompletionHandler<void(ShouldContinue)>&&) override;
56 void dataReceived(PlatformMediaResource&, const char*, int) override;
57 void accessControlCheckFailed(PlatformMediaResource&, const ResourceError&) override;
58 void loadFailed(PlatformMediaResource&, const ResourceError&) override;
59 void loadFinished(PlatformMediaResource&) override;
60
61 static constexpr int s_growBlocksizeLimit { 1 };
62 static constexpr int s_growBlocksizeCount { 1 };
63 static constexpr int s_growBlocksizeFactor { 2 };
64 static constexpr float s_reduceBlocksizeLimit { 0.20 };
65 static constexpr int s_reduceBlocksizeCount { 2 };
66 static constexpr float s_reduceBlocksizeFactor { 0.5 };
67 int m_reduceBlocksizeCount { 0 };
68 int m_increaseBlocksizeCount { 0 };
69
70 GRefPtr<GstElement> m_src;
71 ResourceRequest m_request;
72 HashSet<RefPtr<WebCore::SecurityOrigin>> m_origins;
73};
74
75enum MainThreadSourceNotification {
76 Start = 1 << 0,
77 Stop = 1 << 1,
78 Dispose = 1 << 2,
79};
80
81#define WEBKIT_WEB_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_WEB_SRC, WebKitWebSrcPrivate))
82struct _WebKitWebSrcPrivate {
83 ~_WebKitWebSrcPrivate()
84 {
85 if (notifier && notifier->isValid()) {
86 notifier->notifyAndWait(MainThreadSourceNotification::Dispose, [&] {
87 if (resource) {
88 auto* client = static_cast<CachedResourceStreamingClient*>(resource->client());
89 if (client)
90 client->setSourceElement(nullptr);
91
92 resource->setClient(nullptr);
93 }
94 loader = nullptr;
95 });
96 notifier->invalidate();
97 notifier = nullptr;
98 }
99 }
100
101 CString originalURI;
102 CString redirectedURI;
103 bool keepAlive;
104 GUniquePtr<GstStructure> extraHeaders;
105 bool compress;
106 GUniquePtr<gchar> httpMethod;
107 WebCore::MediaPlayer* player;
108 RefPtr<PlatformMediaResourceLoader> loader;
109 RefPtr<PlatformMediaResource> resource;
110 RefPtr<MainThreadNotifier<MainThreadSourceNotification>> notifier;
111 bool didPassAccessControlCheck;
112 bool wereHeadersReceived;
113 Condition headersCondition;
114 Lock responseLock;
115 bool wasResponseReceived;
116 Condition responseCondition;
117 bool doesHaveEOS;
118 bool isFlushing { false };
119 uint64_t readPosition;
120 uint64_t requestedPosition;
121 uint64_t stopPosition;
122 bool isDurationSet;
123 bool haveSize;
124 uint64_t size;
125 bool isSeekable;
126 bool isSeeking;
127 bool wasSeeking { false };
128 uint64_t minimumBlocksize;
129 Lock adapterLock;
130 Condition adapterCondition;
131 uint64_t queueSize { 0 };
132 GRefPtr<GstAdapter> adapter;
133 GRefPtr<GstEvent> httpHeadersEvent;
134 GUniquePtr<GstStructure> httpHeaders;
135};
136
137enum {
138 PROP_0,
139 PROP_LOCATION,
140 PROP_RESOLVED_LOCATION,
141 PROP_KEEP_ALIVE,
142 PROP_EXTRA_HEADERS,
143 PROP_COMPRESS,
144 PROP_METHOD
145};
146
147static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY);
148
149GST_DEBUG_CATEGORY_STATIC(webkit_web_src_debug);
150#define GST_CAT_DEFAULT webkit_web_src_debug
151
152static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer ifaceData);
153
154static void webKitWebSrcDispose(GObject*);
155static void webKitWebSrcSetProperty(GObject*, guint propertyID, const GValue*, GParamSpec*);
156static void webKitWebSrcGetProperty(GObject*, guint propertyID, GValue*, GParamSpec*);
157static GstStateChangeReturn webKitWebSrcChangeState(GstElement*, GstStateChange);
158static GstFlowReturn webKitWebSrcCreate(GstPushSrc*, GstBuffer**);
159static gboolean webKitWebSrcStart(GstBaseSrc*);
160static gboolean webKitWebSrcStop(GstBaseSrc*);
161static gboolean webKitWebSrcGetSize(GstBaseSrc*, guint64* size);
162static gboolean webKitWebSrcIsSeekable(GstBaseSrc*);
163static gboolean webKitWebSrcDoSeek(GstBaseSrc*, GstSegment*);
164static gboolean webKitWebSrcQuery(GstBaseSrc*, GstQuery*);
165static gboolean webKitWebSrcUnLock(GstBaseSrc*);
166static gboolean webKitWebSrcUnLockStop(GstBaseSrc*);
167static void webKitWebSrcSetContext(GstElement*, GstContext*);
168
169#define webkit_web_src_parent_class parent_class
170// We split this out into another macro to avoid a check-webkit-style error.
171#define WEBKIT_WEB_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_web_src_debug, "webkitwebsrc", 0, "websrc element");
172G_DEFINE_TYPE_WITH_CODE(WebKitWebSrc, webkit_web_src, GST_TYPE_PUSH_SRC,
173 G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitWebSrcUriHandlerInit);
174 WEBKIT_WEB_SRC_CATEGORY_INIT);
175
176static void webkit_web_src_class_init(WebKitWebSrcClass* klass)
177{
178 GObjectClass* oklass = G_OBJECT_CLASS(klass);
179
180 oklass->dispose = webKitWebSrcDispose;
181 oklass->set_property = webKitWebSrcSetProperty;
182 oklass->get_property = webKitWebSrcGetProperty;
183
184 GstElementClass* eklass = GST_ELEMENT_CLASS(klass);
185 gst_element_class_add_static_pad_template(eklass, &srcTemplate);
186
187 gst_element_class_set_metadata(eklass, "WebKit Web source element", "Source", "Handles HTTP/HTTPS uris",
188 "Philippe Normand <philn@igalia.com>");
189
190 /* Allows setting the uri using the 'location' property, which is used
191 * for example by gst_element_make_from_uri() */
192 g_object_class_install_property(oklass, PROP_LOCATION,
193 g_param_spec_string("location", "location", "Location to read from",
194 nullptr, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
195
196 g_object_class_install_property(oklass, PROP_RESOLVED_LOCATION,
197 g_param_spec_string("resolved-location", "Resolved location", "The location resolved by the server",
198 nullptr, static_cast<GParamFlags>(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
199
200 g_object_class_install_property(oklass, PROP_KEEP_ALIVE,
201 g_param_spec_boolean("keep-alive", "keep-alive", "Use HTTP persistent connections",
202 FALSE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
203
204 g_object_class_install_property(oklass, PROP_EXTRA_HEADERS,
205 g_param_spec_boxed("extra-headers", "Extra Headers", "Extra headers to append to the HTTP request",
206 GST_TYPE_STRUCTURE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
207
208 g_object_class_install_property(oklass, PROP_COMPRESS,
209 g_param_spec_boolean("compress", "Compress", "Allow compressed content encodings",
210 FALSE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
211
212 g_object_class_install_property(oklass, PROP_METHOD,
213 g_param_spec_string("method", "method", "The HTTP method to use (default: GET)",
214 nullptr, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
215
216 eklass->change_state = GST_DEBUG_FUNCPTR(webKitWebSrcChangeState);
217 eklass->set_context = GST_DEBUG_FUNCPTR(webKitWebSrcSetContext);
218
219 GstBaseSrcClass* baseSrcClass = GST_BASE_SRC_CLASS(klass);
220 baseSrcClass->start = GST_DEBUG_FUNCPTR(webKitWebSrcStart);
221 baseSrcClass->stop = GST_DEBUG_FUNCPTR(webKitWebSrcStop);
222 baseSrcClass->unlock = GST_DEBUG_FUNCPTR(webKitWebSrcUnLock);
223 baseSrcClass->unlock_stop = GST_DEBUG_FUNCPTR(webKitWebSrcUnLockStop);
224 baseSrcClass->get_size = GST_DEBUG_FUNCPTR(webKitWebSrcGetSize);
225 baseSrcClass->is_seekable = GST_DEBUG_FUNCPTR(webKitWebSrcIsSeekable);
226 baseSrcClass->do_seek = GST_DEBUG_FUNCPTR(webKitWebSrcDoSeek);
227 baseSrcClass->query = GST_DEBUG_FUNCPTR(webKitWebSrcQuery);
228
229 GstPushSrcClass* pushSrcClass = GST_PUSH_SRC_CLASS(klass);
230 pushSrcClass->create = GST_DEBUG_FUNCPTR(webKitWebSrcCreate);
231
232 g_type_class_add_private(klass, sizeof(WebKitWebSrcPrivate));
233}
234
235
236static void webkitWebSrcReset(WebKitWebSrc* src)
237{
238 WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC_GET_PRIVATE(src);
239
240 priv->haveSize = false;
241 priv->wereHeadersReceived = false;
242 priv->isSeekable = false;
243 priv->readPosition = 0;
244 priv->requestedPosition = 0;
245 priv->stopPosition = -1;
246 priv->size = 0;
247}
248
249static void webkit_web_src_init(WebKitWebSrc* src)
250{
251 WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC_GET_PRIVATE(src);
252
253 src->priv = priv;
254 new (priv) WebKitWebSrcPrivate();
255
256 priv->notifier = MainThreadNotifier<MainThreadSourceNotification>::create();
257 priv->adapter = adoptGRef(gst_adapter_new());
258 priv->minimumBlocksize = gst_base_src_get_blocksize(GST_BASE_SRC_CAST(src));
259
260 webkitWebSrcReset(src);
261 gst_base_src_set_automatic_eos(GST_BASE_SRC_CAST(src), FALSE);
262}
263
264static void webKitWebSrcDispose(GObject* object)
265{
266 WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC(object)->priv;
267
268 priv->~WebKitWebSrcPrivate();
269
270 GST_CALL_PARENT(G_OBJECT_CLASS, dispose, (object));
271}
272
273static void webKitWebSrcSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* pspec)
274{
275 WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
276
277 switch (propID) {
278 case PROP_LOCATION:
279 gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), nullptr);
280 break;
281 case PROP_KEEP_ALIVE:
282 src->priv->keepAlive = g_value_get_boolean(value);
283 break;
284 case PROP_EXTRA_HEADERS: {
285 const GstStructure* s = gst_value_get_structure(value);
286 src->priv->extraHeaders.reset(s ? gst_structure_copy(s) : nullptr);
287 break;
288 }
289 case PROP_COMPRESS:
290 src->priv->compress = g_value_get_boolean(value);
291 break;
292 case PROP_METHOD:
293 src->priv->httpMethod.reset(g_value_dup_string(value));
294 break;
295 default:
296 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
297 break;
298 }
299}
300
301static void webKitWebSrcGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* pspec)
302{
303 WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
304 WebKitWebSrcPrivate* priv = src->priv;
305
306 switch (propID) {
307 case PROP_LOCATION:
308 g_value_set_string(value, priv->originalURI.data());
309 break;
310 case PROP_RESOLVED_LOCATION:
311 g_value_set_string(value, priv->redirectedURI.isNull() ? priv->originalURI.data() : priv->redirectedURI.data());
312 break;
313 case PROP_KEEP_ALIVE:
314 g_value_set_boolean(value, priv->keepAlive);
315 break;
316 case PROP_EXTRA_HEADERS:
317 gst_value_set_structure(value, priv->extraHeaders.get());
318 break;
319 case PROP_COMPRESS:
320 g_value_set_boolean(value, priv->compress);
321 break;
322 case PROP_METHOD:
323 g_value_set_string(value, priv->httpMethod.get());
324 break;
325 default:
326 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
327 break;
328 }
329}
330
331static void webKitWebSrcSetContext(GstElement* element, GstContext* context)
332{
333 WebKitWebSrc* src = WEBKIT_WEB_SRC(element);
334 WebKitWebSrcPrivate* priv = src->priv;
335
336 GST_DEBUG_OBJECT(src, "context type: %s", gst_context_get_context_type(context));
337 if (gst_context_has_context_type(context, WEBKIT_WEB_SRC_PLAYER_CONTEXT_TYPE_NAME)) {
338 const GValue* value = gst_structure_get_value(gst_context_get_structure(context), "player");
339 priv->player = reinterpret_cast<MediaPlayer*>(g_value_get_pointer(value));
340 }
341 GST_ELEMENT_CLASS(parent_class)->set_context(element, context);
342}
343
344static GstFlowReturn webKitWebSrcCreate(GstPushSrc* pushSrc, GstBuffer** buffer)
345{
346 GstBaseSrc* baseSrc = GST_BASE_SRC_CAST(pushSrc);
347 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc);
348 WebKitWebSrcPrivate* priv = src->priv;
349
350 GST_TRACE_OBJECT(src, "readPosition = %" G_GUINT64_FORMAT " requestedPosition = %" G_GUINT64_FORMAT, priv->readPosition, priv->requestedPosition);
351
352 if (priv->requestedPosition != priv->readPosition) {
353 {
354 LockHolder adapterLocker(priv->adapterLock);
355 GST_DEBUG_OBJECT(src, "Seeking, flushing adapter");
356 // Discard all the buffers coming before the requested seek position.
357 gst_adapter_flush(priv->adapter.get(), priv->queueSize);
358 priv->queueSize = 0;
359 }
360 uint64_t requestedPosition = priv->requestedPosition;
361 webKitWebSrcStop(baseSrc);
362 priv->requestedPosition = requestedPosition;
363 webKitWebSrcStart(baseSrc);
364 }
365
366 {
367 LockHolder locker(priv->responseLock);
368 priv->responseCondition.wait(priv->responseLock, [priv] () {
369 return priv->wasResponseReceived || priv->isFlushing;
370 });
371 }
372
373 GST_TRACE_OBJECT(src, "flushing: %s, doesHaveEOS: %s, queueSize: %" G_GSIZE_FORMAT, boolForPrinting(priv->isFlushing), boolForPrinting(priv->doesHaveEOS), priv->queueSize);
374
375 if (priv->isFlushing) {
376 GST_DEBUG_OBJECT(src, "Flushing");
377 return GST_FLOW_FLUSHING;
378 }
379
380 if (priv->doesHaveEOS) {
381 GST_DEBUG_OBJECT(src, "EOS");
382 return GST_FLOW_EOS;
383 }
384
385 unsigned size = gst_base_src_get_blocksize(baseSrc);
386 bool isAdapterDrained = false;
387 {
388 LockHolder adapterLocker(priv->adapterLock);
389 unsigned retries = 0;
390 size_t available = gst_adapter_available_fast(priv->adapter.get());
391 while (available < size && !isAdapterDrained) {
392 priv->adapterCondition.waitFor(priv->adapterLock, Seconds(1));
393 retries++;
394 available = gst_adapter_available_fast(priv->adapter.get());
395 if (available && available < size)
396 size = available;
397 else if (retries > 3)
398 isAdapterDrained = true;
399 }
400 }
401
402 if (isAdapterDrained) {
403 GST_DEBUG_OBJECT(src, "Adapter still empty after 3 seconds of waiting, assuming EOS");
404 return GST_FLOW_EOS;
405 }
406
407 if (priv->haveSize && !priv->isDurationSet) {
408 GST_DEBUG_OBJECT(src, "Setting duration to %" G_GUINT64_FORMAT, priv->size);
409 baseSrc->segment.duration = priv->size;
410 priv->isDurationSet = true;
411 gst_element_post_message(GST_ELEMENT_CAST(src), gst_message_new_duration_changed(GST_OBJECT_CAST(src)));
412 }
413
414 if (priv->httpHeadersEvent)
415 gst_pad_push_event(GST_BASE_SRC_PAD(baseSrc), priv->httpHeadersEvent.leakRef());
416
417 {
418 GST_TRACE_OBJECT(src, "Taking %u bytes from adapter", size);
419 LockHolder adapterLocker(priv->adapterLock);
420 if (size) {
421 *buffer = gst_adapter_take_buffer_fast(priv->adapter.get(), size);
422 RELEASE_ASSERT(*buffer);
423
424 priv->queueSize -= size;
425
426 GST_BUFFER_OFFSET(*buffer) = baseSrc->segment.position;
427 GST_BUFFER_OFFSET_END(*buffer) = GST_BUFFER_OFFSET(*buffer) + size;
428 GST_TRACE_OBJECT(src, "Buffer bounds set to %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT, GST_BUFFER_OFFSET(*buffer), GST_BUFFER_OFFSET_END(*buffer));
429 GST_TRACE_OBJECT(src, "doesHaveEOS: %s, wasSeeking: %s, seeking: %s, size: %u", boolForPrinting(priv->doesHaveEOS), boolForPrinting(priv->wasSeeking), boolForPrinting(priv->isSeeking), size);
430 if (priv->haveSize && GST_BUFFER_OFFSET_END(*buffer) >= priv->size) {
431 if (priv->wasSeeking)
432 priv->wasSeeking = false;
433 else
434 priv->doesHaveEOS = true;
435 } else if (priv->wasSeeking)
436 priv->wasSeeking = false;
437 } else
438 GST_ERROR_OBJECT(src, "Empty adapter?");
439 }
440
441 return GST_FLOW_OK;
442}
443
444static bool webKitWebSrcSetExtraHeader(GQuark fieldId, const GValue* value, gpointer userData)
445{
446 GUniquePtr<gchar> fieldContent;
447
448 if (G_VALUE_HOLDS_STRING(value))
449 fieldContent.reset(g_value_dup_string(value));
450 else {
451 GValue dest = G_VALUE_INIT;
452
453 g_value_init(&dest, G_TYPE_STRING);
454 if (g_value_transform(value, &dest))
455 fieldContent.reset(g_value_dup_string(&dest));
456 }
457
458 const gchar* fieldName = g_quark_to_string(fieldId);
459 if (!fieldContent.get()) {
460 GST_ERROR("extra-headers field '%s' contains no value or can't be converted to a string", fieldName);
461 return false;
462 }
463
464 GST_DEBUG("Appending extra header: \"%s: %s\"", fieldName, fieldContent.get());
465 ResourceRequest* request = static_cast<ResourceRequest*>(userData);
466 request->setHTTPHeaderField(fieldName, fieldContent.get());
467 return true;
468}
469
470static gboolean webKitWebSrcProcessExtraHeaders(GQuark fieldId, const GValue* value, gpointer userData)
471{
472 if (G_VALUE_TYPE(value) == GST_TYPE_ARRAY) {
473 unsigned size = gst_value_array_get_size(value);
474
475 for (unsigned i = 0; i < size; i++) {
476 if (!webKitWebSrcSetExtraHeader(fieldId, gst_value_array_get_value(value, i), userData))
477 return FALSE;
478 }
479 return TRUE;
480 }
481
482 if (G_VALUE_TYPE(value) == GST_TYPE_LIST) {
483 unsigned size = gst_value_list_get_size(value);
484
485 for (unsigned i = 0; i < size; i++) {
486 if (!webKitWebSrcSetExtraHeader(fieldId, gst_value_list_get_value(value, i), userData))
487 return FALSE;
488 }
489 return TRUE;
490 }
491
492 return webKitWebSrcSetExtraHeader(fieldId, value, userData);
493}
494
495static gboolean webKitWebSrcStart(GstBaseSrc* baseSrc)
496{
497 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc);
498 WebKitWebSrcPrivate* priv = src->priv;
499
500 if (webkitGstCheckVersion(1, 12, 0) && !priv->player) {
501 GRefPtr<GstQuery> query = adoptGRef(gst_query_new_context(WEBKIT_WEB_SRC_PLAYER_CONTEXT_TYPE_NAME));
502 if (gst_pad_peer_query(GST_BASE_SRC_PAD(baseSrc), query.get())) {
503 GstContext* context;
504
505 gst_query_parse_context(query.get(), &context);
506 gst_element_set_context(GST_ELEMENT_CAST(src), context);
507 } else
508 gst_element_post_message(GST_ELEMENT_CAST(src), gst_message_new_need_context(GST_OBJECT_CAST(src), WEBKIT_WEB_SRC_PLAYER_CONTEXT_TYPE_NAME));
509 }
510
511 RELEASE_ASSERT(priv->player);
512
513 priv->wereHeadersReceived = false;
514 priv->wasResponseReceived = false;
515 priv->isDurationSet = false;
516 priv->doesHaveEOS = false;
517 priv->isFlushing = false;
518
519 priv->didPassAccessControlCheck = false;
520
521 if (priv->originalURI.isNull()) {
522 GST_ERROR_OBJECT(src, "No URI provided");
523 webKitWebSrcStop(baseSrc);
524 return FALSE;
525 }
526
527 if (priv->requestedPosition == priv->stopPosition) {
528 GST_DEBUG_OBJECT(src, "Empty segment, signaling EOS");
529 priv->doesHaveEOS = true;
530 return FALSE;
531 }
532
533 GST_DEBUG_OBJECT(src, "Fetching %s", priv->originalURI.data());
534 URL url = URL(URL(), priv->originalURI.data());
535
536 ResourceRequest request(url);
537 request.setAllowCookies(true);
538 request.setFirstPartyForCookies(url);
539
540 request.setHTTPReferrer(priv->player->referrer());
541
542 if (priv->httpMethod.get())
543 request.setHTTPMethod(priv->httpMethod.get());
544
545#if USE(SOUP)
546 // By default, HTTP Accept-Encoding is disabled here as we don't
547 // want the received response to be encoded in any way as we need
548 // to rely on the proper size of the returned data on
549 // didReceiveResponse.
550 // If Accept-Encoding is used, the server may send the data in encoded format and
551 // request.expectedContentLength() will have the "wrong" size (the size of the
552 // compressed data), even though the data received in didReceiveData is uncompressed.
553 // This is however useful to enable for adaptive streaming
554 // scenarios, when the demuxer needs to download playlists.
555 if (!priv->compress)
556 request.setAcceptEncoding(false);
557#endif
558
559 // Let Apple web servers know we want to access their nice movie trailers.
560 if (!g_ascii_strcasecmp("movies.apple.com", url.host().utf8().data())
561 || !g_ascii_strcasecmp("trailers.apple.com", url.host().utf8().data()))
562 request.setHTTPUserAgent("Quicktime/7.6.6");
563
564 if (priv->requestedPosition) {
565 GUniquePtr<char> formatedRange(g_strdup_printf("bytes=%" G_GUINT64_FORMAT "-", priv->requestedPosition));
566 GST_DEBUG_OBJECT(src, "Range request: %s", formatedRange.get());
567 request.setHTTPHeaderField(HTTPHeaderName::Range, formatedRange.get());
568 }
569 priv->readPosition = priv->requestedPosition;
570
571 GST_DEBUG_OBJECT(src, "Persistent connection support %s", priv->keepAlive ? "enabled" : "disabled");
572 if (!priv->keepAlive)
573 request.setHTTPHeaderField(HTTPHeaderName::Connection, "close");
574
575 if (priv->extraHeaders)
576 gst_structure_foreach(priv->extraHeaders.get(), webKitWebSrcProcessExtraHeaders, &request);
577
578 // We always request Icecast/Shoutcast metadata, just in case ...
579 request.setHTTPHeaderField(HTTPHeaderName::IcyMetadata, "1");
580
581 GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
582 priv->notifier->notifyAndWait(MainThreadSourceNotification::Start, [protector, request = WTFMove(request)] {
583 WebKitWebSrcPrivate* priv = protector->priv;
584 if (!priv->loader)
585 priv->loader = priv->player->createResourceLoader();
586
587 PlatformMediaResourceLoader::LoadOptions loadOptions = 0;
588 if (request.url().protocolIsBlob())
589 loadOptions |= PlatformMediaResourceLoader::LoadOption::BufferData;
590 priv->resource = priv->loader->requestResource(ResourceRequest(request), loadOptions);
591 if (priv->resource) {
592 priv->resource->setClient(std::make_unique<CachedResourceStreamingClient>(protector.get(), ResourceRequest(request)));
593 GST_DEBUG_OBJECT(protector.get(), "Started request");
594 } else {
595 GST_ERROR_OBJECT(protector.get(), "Failed to setup streaming client");
596 priv->loader = nullptr;
597 }
598 });
599
600 GST_DEBUG_OBJECT(src, "Resource loader started");
601 return TRUE;
602}
603
604static void webKitWebSrcCloseSession(WebKitWebSrc* src)
605{
606 WebKitWebSrcPrivate* priv = src->priv;
607 GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
608
609 priv->notifier->notifyAndWait(MainThreadSourceNotification::Stop, [protector, keepAlive = priv->keepAlive] {
610 WebKitWebSrcPrivate* priv = protector->priv;
611
612 GST_DEBUG_OBJECT(protector.get(), "Stopping resource loader");
613
614 if (priv->resource) {
615 priv->resource->stop();
616 priv->resource->setClient(nullptr);
617 priv->resource = nullptr;
618 }
619
620 if (!keepAlive)
621 priv->loader = nullptr;
622 });
623
624 GST_DEBUG_OBJECT(src, "Resource loader stopped");
625}
626
627static gboolean webKitWebSrcStop(GstBaseSrc* baseSrc)
628{
629 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc);
630 WebKitWebSrcPrivate* priv = src->priv;
631
632 if (priv->resource || (priv->loader && !priv->keepAlive))
633 webKitWebSrcCloseSession(src);
634
635 {
636 LockHolder adapterLocker(priv->adapterLock);
637 gst_adapter_clear(priv->adapter.get());
638 priv->queueSize = 0;
639 }
640
641 webkitWebSrcReset(src);
642 GST_DEBUG_OBJECT(src, "Stopped request");
643 return TRUE;
644}
645
646static gboolean webKitWebSrcGetSize(GstBaseSrc* baseSrc, guint64* size)
647{
648 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc);
649 WebKitWebSrcPrivate* priv = src->priv;
650
651 GST_DEBUG_OBJECT(src, "haveSize: %s, size: %" G_GUINT64_FORMAT, boolForPrinting(priv->haveSize), priv->size);
652 if (priv->haveSize) {
653 *size = priv->size;
654 return TRUE;
655 }
656
657 return FALSE;
658}
659
660static gboolean webKitWebSrcIsSeekable(GstBaseSrc* baseSrc)
661{
662 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc);
663
664 GST_DEBUG_OBJECT(src, "isSeekable: %s", boolForPrinting(src->priv->isSeekable));
665 return src->priv->isSeekable;
666}
667
668static gboolean webKitWebSrcDoSeek(GstBaseSrc* baseSrc, GstSegment* segment)
669{
670 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc);
671 WebKitWebSrcPrivate* priv = src->priv;
672 LockHolder locker(priv->responseLock);
673
674 GST_DEBUG_OBJECT(src, "Seek segment: (%" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT ")", segment->start, segment->stop);
675 if (priv->readPosition == segment->start && priv->requestedPosition == priv->readPosition && priv->stopPosition == segment->stop) {
676 GST_DEBUG_OBJECT(src, "Seek to current read/end position and no seek pending");
677 return TRUE;
678 }
679
680 if (priv->wereHeadersReceived && !priv->isSeekable) {
681 GST_WARNING_OBJECT(src, "Not seekable");
682 return FALSE;
683 }
684
685 if (segment->rate < 0.0 || segment->format != GST_FORMAT_BYTES) {
686 GST_WARNING_OBJECT(src, "Invalid seek segment");
687 return FALSE;
688 }
689
690 if (priv->haveSize && segment->start >= priv->size)
691 GST_WARNING_OBJECT(src, "Potentially seeking behind end of file, might EOS immediately");
692
693 priv->isSeeking = true;
694 priv->requestedPosition = segment->start;
695 priv->stopPosition = segment->stop;
696 return TRUE;
697}
698
699static gboolean webKitWebSrcQuery(GstBaseSrc* baseSrc, GstQuery* query)
700{
701 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc);
702 WebKitWebSrcPrivate* priv = src->priv;
703 gboolean result = FALSE;
704
705 if (GST_QUERY_TYPE(query) == GST_QUERY_URI) {
706 gst_query_set_uri(query, priv->originalURI.data());
707 if (!priv->redirectedURI.isNull())
708 gst_query_set_uri_redirection(query, priv->redirectedURI.data());
709 result = TRUE;
710 }
711
712 if (!result)
713 result = GST_BASE_SRC_CLASS(parent_class)->query(baseSrc, query);
714
715 if (GST_QUERY_TYPE(query) == GST_QUERY_SCHEDULING) {
716 GstSchedulingFlags flags;
717 int minSize, maxSize, align;
718
719 gst_query_parse_scheduling(query, &flags, &minSize, &maxSize, &align);
720 gst_query_set_scheduling(query, static_cast<GstSchedulingFlags>(flags | GST_SCHEDULING_FLAG_BANDWIDTH_LIMITED), minSize, maxSize, align);
721 }
722
723 return result;
724}
725
726static gboolean webKitWebSrcUnLock(GstBaseSrc* baseSrc)
727{
728 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc);
729 LockHolder locker(src->priv->responseLock);
730
731 GST_DEBUG_OBJECT(src, "Unlock");
732 src->priv->isFlushing = true;
733 src->priv->responseCondition.notifyOne();
734 return TRUE;
735}
736
737static gboolean webKitWebSrcUnLockStop(GstBaseSrc* baseSrc)
738{
739 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc);
740 LockHolder locker(src->priv->responseLock);
741 GST_DEBUG_OBJECT(src, "Unlock stop");
742 src->priv->isFlushing = false;
743
744 return TRUE;
745}
746
747static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStateChange transition)
748{
749 WebKitWebSrc* src = WEBKIT_WEB_SRC(element);
750
751#if GST_CHECK_VERSION(1, 14, 0)
752 GST_DEBUG_OBJECT(src, "%s", gst_state_change_get_name(transition));
753#endif
754 switch (transition) {
755 case GST_STATE_CHANGE_READY_TO_NULL:
756 webKitWebSrcCloseSession(src);
757 break;
758 default:
759 break;
760 }
761
762 return GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
763}
764
765static bool urlHasSupportedProtocol(const URL& url)
766{
767 return url.isValid() && (url.protocolIsInHTTPFamily() || url.protocolIsBlob());
768}
769
770// uri handler interface
771
772static GstURIType webKitWebSrcUriGetType(GType)
773{
774 return GST_URI_SRC;
775}
776
777const gchar* const* webKitWebSrcGetProtocols(GType)
778{
779 static const char* protocols[4];
780 if (webkitGstCheckVersion(1, 12, 0)) {
781 protocols[0] = "http";
782 protocols[1] = "https";
783 protocols[2] = "blob";
784 } else {
785 protocols[0] = "webkit+http";
786 protocols[1] = "webkit+https";
787 protocols[2] = "webkit+blob";
788 }
789 protocols[3] = nullptr;
790 return protocols;
791}
792
793static URL convertPlaybinURI(const char* uriString)
794{
795 URL url(URL(), uriString);
796 if (!webkitGstCheckVersion(1, 12, 0)) {
797 ASSERT(url.protocol().substring(0, 7) == "webkit+");
798 url.setProtocol(url.protocol().substring(7).toString());
799 }
800 return url;
801}
802
803static gchar* webKitWebSrcGetUri(GstURIHandler* handler)
804{
805 WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
806 gchar* ret = g_strdup(src->priv->originalURI.data());
807 return ret;
808}
809
810static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GError** error)
811{
812 WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
813 WebKitWebSrcPrivate* priv = src->priv;
814
815 if (GST_STATE(src) >= GST_STATE_PAUSED) {
816 GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED");
817 return FALSE;
818 }
819
820 priv->redirectedURI = CString();
821 priv->originalURI = CString();
822 if (!uri)
823 return TRUE;
824
825 if (priv->originalURI.length()) {
826 GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED");
827 return FALSE;
828 }
829
830 URL url = convertPlaybinURI(uri);
831
832 if (!urlHasSupportedProtocol(url)) {
833 g_set_error(error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI, "Invalid URI '%s'", uri);
834 return FALSE;
835 }
836
837 priv->originalURI = url.string().utf8();
838 return TRUE;
839}
840
841static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer)
842{
843 GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface;
844
845 iface->get_type = webKitWebSrcUriGetType;
846 iface->get_protocols = webKitWebSrcGetProtocols;
847 iface->get_uri = webKitWebSrcGetUri;
848 iface->set_uri = webKitWebSrcSetUri;
849}
850
851void webKitWebSrcSetMediaPlayer(WebKitWebSrc* src, WebCore::MediaPlayer* player)
852{
853 ASSERT(player);
854 src->priv->player = player;
855}
856
857bool webKitSrcPassedCORSAccessCheck(WebKitWebSrc* src)
858{
859 return src->priv->didPassAccessControlCheck;
860}
861
862CachedResourceStreamingClient::CachedResourceStreamingClient(WebKitWebSrc* src, ResourceRequest&& request)
863 : m_src(GST_ELEMENT(src))
864 , m_request(WTFMove(request))
865{
866}
867
868CachedResourceStreamingClient::~CachedResourceStreamingClient() = default;
869
870void CachedResourceStreamingClient::checkUpdateBlocksize(uint64_t bytesRead)
871{
872 WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
873 GstBaseSrc* baseSrc = GST_BASE_SRC_CAST(src);
874 WebKitWebSrcPrivate* priv = src->priv;
875
876 uint64_t blocksize = gst_base_src_get_blocksize(baseSrc);
877 GST_LOG_OBJECT(src, "Checking to update blocksize. Read: %" PRIu64 ", current blocksize: %" PRIu64, bytesRead, blocksize);
878
879 if (bytesRead >= blocksize * s_growBlocksizeLimit) {
880 m_reduceBlocksizeCount = 0;
881 m_increaseBlocksizeCount++;
882
883 if (m_increaseBlocksizeCount >= s_growBlocksizeCount) {
884 blocksize *= s_growBlocksizeFactor;
885 GST_DEBUG_OBJECT(src, "Increased blocksize to %" PRIu64, blocksize);
886 gst_base_src_set_blocksize(baseSrc, blocksize);
887 m_increaseBlocksizeCount = 0;
888 }
889 } else if (bytesRead < blocksize * s_reduceBlocksizeLimit) {
890 m_reduceBlocksizeCount++;
891 m_increaseBlocksizeCount = 0;
892
893 if (m_reduceBlocksizeCount >= s_reduceBlocksizeCount) {
894 blocksize *= s_reduceBlocksizeFactor;
895 blocksize = std::max(blocksize, priv->minimumBlocksize);
896 GST_DEBUG_OBJECT(src, "Decreased blocksize to %" PRIu64, blocksize);
897 gst_base_src_set_blocksize(baseSrc, blocksize);
898 m_reduceBlocksizeCount = 0;
899 }
900 } else {
901 m_reduceBlocksizeCount = 0;
902 m_increaseBlocksizeCount = 0;
903 }
904}
905
906void CachedResourceStreamingClient::responseReceived(PlatformMediaResource&, const ResourceResponse& response, CompletionHandler<void(ShouldContinue)>&& completionHandler)
907{
908 WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
909 WebKitWebSrcPrivate* priv = src->priv;
910 priv->didPassAccessControlCheck = priv->resource->didPassAccessControlCheck();
911
912 GST_DEBUG_OBJECT(src, "Received response: %d", response.httpStatusCode());
913
914 m_origins.add(SecurityOrigin::create(response.url()));
915
916 auto responseURI = response.url().string().utf8();
917 if (priv->originalURI != responseURI)
918 priv->redirectedURI = WTFMove(responseURI);
919
920 uint64_t length = response.expectedContentLength();
921 if (length > 0 && priv->requestedPosition && response.httpStatusCode() == 206)
922 length += priv->requestedPosition;
923
924 priv->httpHeaders.reset(gst_structure_new_empty("http-headers"));
925 gst_structure_set(priv->httpHeaders.get(), "uri", G_TYPE_STRING, priv->originalURI.data(),
926 "http-status-code", G_TYPE_UINT, response.httpStatusCode(), nullptr);
927 if (!priv->redirectedURI.isNull())
928 gst_structure_set(priv->httpHeaders.get(), "redirection-uri", G_TYPE_STRING, priv->redirectedURI.data(), nullptr);
929 GUniquePtr<GstStructure> headers(gst_structure_new_empty("request-headers"));
930 for (const auto& header : m_request.httpHeaderFields())
931 gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_STRING, header.value.utf8().data(), nullptr);
932 GST_DEBUG_OBJECT(src, "Request headers going downstream: %" GST_PTR_FORMAT, headers.get());
933 gst_structure_set(priv->httpHeaders.get(), "request-headers", GST_TYPE_STRUCTURE, headers.get(), nullptr);
934 headers.reset(gst_structure_new_empty("response-headers"));
935 for (const auto& header : response.httpHeaderFields()) {
936 bool ok = false;
937 uint64_t convertedValue = header.value.toUInt64(&ok);
938 if (ok)
939 gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_UINT64, convertedValue, nullptr);
940 else
941 gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_STRING, header.value.utf8().data(), nullptr);
942 }
943 auto contentLengthFieldName(httpHeaderNameString(HTTPHeaderName::ContentLength).toString());
944 if (!gst_structure_has_field(headers.get(), contentLengthFieldName.utf8().data()))
945 gst_structure_set(headers.get(), contentLengthFieldName.utf8().data(), G_TYPE_UINT64, static_cast<uint64_t>(length), nullptr);
946 gst_structure_set(priv->httpHeaders.get(), "response-headers", GST_TYPE_STRUCTURE, headers.get(), nullptr);
947 GST_DEBUG_OBJECT(src, "Response headers going downstream: %" GST_PTR_FORMAT, headers.get());
948
949 priv->httpHeadersEvent = adoptGRef(gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM_STICKY, gst_structure_copy(priv->httpHeaders.get())));
950
951 auto scopeExit = makeScopeExit([&] {
952 GstStructure* structure = gst_structure_copy(src->priv->httpHeaders.get());
953 gst_element_post_message(GST_ELEMENT_CAST(src), gst_message_new_element(GST_OBJECT_CAST(src), structure));
954 });
955
956 if (response.httpStatusCode() >= 400) {
957 GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received %d HTTP error code", response.httpStatusCode()), (nullptr));
958 priv->doesHaveEOS = true;
959 webKitWebSrcStop(GST_BASE_SRC_CAST(src));
960 completionHandler(ShouldContinue::No);
961 return;
962 }
963
964 if (priv->requestedPosition) {
965 // Seeking ... we expect a 206 == PARTIAL_CONTENT
966 if (response.httpStatusCode() == 200) {
967 // Range request didn't have a ranged response; resetting offset.
968 priv->readPosition = 0;
969 } else if (response.httpStatusCode() != 206) {
970 // Range request completely failed.
971 GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received unexpected %d HTTP status code", response.httpStatusCode()), (nullptr));
972 priv->doesHaveEOS = true;
973 webKitWebSrcStop(GST_BASE_SRC_CAST(src));
974 completionHandler(ShouldContinue::No);
975 return;
976 } else {
977 GST_DEBUG_OBJECT(src, "Range request succeeded");
978 priv->isSeeking = false;
979 priv->wasSeeking = true;
980 }
981 }
982
983 priv->isSeekable = length > 0 && g_ascii_strcasecmp("none", response.httpHeaderField(HTTPHeaderName::AcceptRanges).utf8().data());
984
985 GST_DEBUG_OBJECT(src, "Size: %" G_GUINT64_FORMAT ", isSeekable: %s", length, boolForPrinting(priv->isSeekable));
986 if (length > 0) {
987 if (!priv->haveSize || priv->size != length) {
988 priv->haveSize = true;
989 priv->size = length;
990 priv->isDurationSet = false;
991 }
992 }
993
994 // Signal to downstream if this is an Icecast stream.
995 GRefPtr<GstCaps> caps;
996 String metadataIntervalAsString = response.httpHeaderField(HTTPHeaderName::IcyMetaInt);
997 if (!metadataIntervalAsString.isEmpty()) {
998 bool isMetadataIntervalParsed;
999 int metadataInterval = metadataIntervalAsString.toInt(&isMetadataIntervalParsed);
1000 if (isMetadataIntervalParsed && metadataInterval > 0) {
1001 caps = adoptGRef(gst_caps_new_simple("application/x-icy", "metadata-interval", G_TYPE_INT, metadataInterval, nullptr));
1002
1003 String contentType = response.httpHeaderField(HTTPHeaderName::ContentType);
1004 GST_DEBUG_OBJECT(src, "Response ContentType: %s", contentType.utf8().data());
1005 gst_caps_set_simple(caps.get(), "content-type", G_TYPE_STRING, contentType.utf8().data(), nullptr);
1006 }
1007 }
1008
1009 if (caps) {
1010 GST_DEBUG_OBJECT(src, "Set caps to %" GST_PTR_FORMAT, caps.get());
1011 gst_base_src_set_caps(GST_BASE_SRC_CAST(src), caps.get());
1012 }
1013
1014 {
1015 LockHolder locker(priv->responseLock);
1016 priv->wereHeadersReceived = true;
1017 priv->headersCondition.notifyOne();
1018 }
1019 completionHandler(ShouldContinue::Yes);
1020}
1021
1022void CachedResourceStreamingClient::dataReceived(PlatformMediaResource&, const char* data, int length)
1023{
1024 WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
1025 GstBaseSrc* baseSrc = GST_BASE_SRC_CAST(src);
1026 WebKitWebSrcPrivate* priv = src->priv;
1027
1028 GST_LOG_OBJECT(src, "Have %d bytes of data", length);
1029 LockHolder locker(priv->responseLock);
1030
1031 uint64_t newPosition = priv->readPosition + length;
1032 if (LIKELY (priv->requestedPosition == priv->readPosition))
1033 priv->requestedPosition = newPosition;
1034 priv->readPosition = newPosition;
1035
1036 uint64_t newSize = 0;
1037 if (priv->haveSize && (newPosition > priv->size)) {
1038 GST_DEBUG_OBJECT(src, "Got position previous estimated content size (%" G_GINT64_FORMAT " > %" G_GINT64_FORMAT ")", newPosition, priv->size);
1039 newSize = newPosition;
1040 } else if (!priv->haveSize) {
1041 GST_DEBUG_OBJECT(src, "Got initial response without Content-Length, assuming response size as duration.");
1042 newSize = length;
1043 priv->haveSize = true;
1044 }
1045
1046 if (newSize) {
1047 priv->size = newSize;
1048 baseSrc->segment.duration = priv->size;
1049 gst_element_post_message(GST_ELEMENT_CAST(src), gst_message_new_duration_changed(GST_OBJECT_CAST(src)));
1050 }
1051
1052 gst_element_post_message(GST_ELEMENT_CAST(src), gst_message_new_element(GST_OBJECT_CAST(src),
1053 gst_structure_new("webkit-network-statistics", "read-position", G_TYPE_UINT64, priv->readPosition, "size", G_TYPE_UINT64, priv->size, nullptr)));
1054
1055 checkUpdateBlocksize(length);
1056
1057 if (!priv->wasResponseReceived)
1058 priv->wasResponseReceived = true;
1059 priv->responseCondition.notifyOne();
1060
1061 {
1062 LockHolder adapterLocker(priv->adapterLock);
1063 GstBuffer* buffer = gst_buffer_new_wrapped(g_memdup(data, length), length);
1064 priv->queueSize += length;
1065 gst_adapter_push(priv->adapter.get(), buffer);
1066 priv->adapterCondition.notifyOne();
1067 }
1068}
1069
1070void CachedResourceStreamingClient::accessControlCheckFailed(PlatformMediaResource&, const ResourceError& error)
1071{
1072 WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
1073 GST_ELEMENT_ERROR(src, RESOURCE, READ, ("%s", error.localizedDescription().utf8().data()), (nullptr));
1074 src->priv->doesHaveEOS = true;
1075 webKitWebSrcStop(GST_BASE_SRC_CAST(src));
1076}
1077
1078void CachedResourceStreamingClient::loadFailed(PlatformMediaResource&, const ResourceError& error)
1079{
1080 WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
1081
1082 if (!error.isCancellation()) {
1083 GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data());
1084 GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (nullptr));
1085 }
1086
1087 src->priv->doesHaveEOS = true;
1088}
1089
1090void CachedResourceStreamingClient::loadFinished(PlatformMediaResource&)
1091{
1092 WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
1093 WebKitWebSrcPrivate* priv = src->priv;
1094
1095 if (priv->isSeeking && !priv->isFlushing)
1096 priv->isSeeking = false;
1097}
1098
1099bool webKitSrcWouldTaintOrigin(WebKitWebSrc* src, const SecurityOrigin& origin)
1100{
1101 WebKitWebSrcPrivate* priv = src->priv;
1102
1103 auto* cachedResourceStreamingClient = reinterpret_cast<CachedResourceStreamingClient*>(priv->resource->client());
1104 for (auto& responseOrigin : cachedResourceStreamingClient->securityOrigins()) {
1105 if (!origin.canAccess(*responseOrigin))
1106 return true;
1107 }
1108 return false;
1109}
1110
1111#endif // ENABLE(VIDEO) && USE(GSTREAMER)
1112