• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2007-2008 Wouter Cloetens <wouter@mind.be>
3  * Copyright (C) 2021 Igalia S.L.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more
14  */
15 
16 /**
17  * SECTION:element-souphttpsrc
18  * @title: souphttpsrc
19  *
20  * This plugin reads data from a remote location specified by a URI.
21  * Supported protocols are 'http', 'https'.
22  *
23  * An HTTP proxy must be specified by its URL.
24  * If the "http_proxy" environment variable is set, its value is used.
25  * If built with libsoup's GNOME integration features, the GNOME proxy
26  * configuration will be used, or failing that, proxy autodetection.
27  * The #GstSoupHTTPSrc:proxy property can be used to override the default.
28  *
29  * In case the #GstSoupHTTPSrc:iradio-mode property is set and the location is
30  * an HTTP resource, souphttpsrc will send special Icecast HTTP headers to the
31  * server to request additional Icecast meta-information.
32  * If the server is not an Icecast server, it will behave as if the
33  * #GstSoupHTTPSrc:iradio-mode property were not set. If it is, souphttpsrc will
34  * output data with a media type of application/x-icy, in which case you will
35  * need to use the #GstICYDemux element as follow-up element to extract the Icecast
36  * metadata and to determine the underlying media type.
37  *
38  * ## Example launch line
39  * |[
40  * gst-launch-1.0 -v souphttpsrc location=https://some.server.org/index.html
41  *     ! filesink location=/home/joe/server.html
42  * ]| The above pipeline reads a web page from a server using the HTTPS protocol
43  * and writes it to a local file.
44  * |[
45  * gst-launch-1.0 -v souphttpsrc user-agent="FooPlayer 0.99 beta"
46  *     automatic-redirect=false proxy=http://proxy.intranet.local:8080
47  *     location=http://music.foobar.com/demo.mp3 ! mpgaudioparse
48  *     ! mpg123audiodec ! audioconvert ! audioresample ! autoaudiosink
49  * ]| The above pipeline will read and decode and play an mp3 file from a
50  * web server using the HTTP protocol. If the server sends redirects,
51  * the request fails instead of following the redirect. The specified
52  * HTTP proxy server is used. The User-Agent HTTP request header
53  * is set to a custom string instead of "GStreamer souphttpsrc."
54  * |[
55  * gst-launch-1.0 -v souphttpsrc location=http://10.11.12.13/mjpeg
56  *     do-timestamp=true ! multipartdemux
57  *     ! image/jpeg,width=640,height=480 ! matroskamux
58  *     ! filesink location=mjpeg.mkv
59  * ]| The above pipeline reads a motion JPEG stream from an IP camera
60  * using the HTTP protocol, encoded as mime/multipart image/jpeg
61  * parts, and writes a Matroska motion JPEG file. The width and
62  * height properties are set in the caps to provide the Matroska
63  * multiplexer with the information to set this in the header.
64  * Timestamps are set on the buffers as they arrive from the camera.
65  * These are used by the mime/multipart demultiplexer to emit timestamps
66  * on the JPEG-encoded video frame buffers. This allows the Matroska
67  * multiplexer to timestamp the frames in the resulting file.
68  *
69  */
70 
71 #ifdef HAVE_CONFIG_H
72 #include "config.h"
73 #endif
74 
75 #include <string.h>
76 #ifdef HAVE_STDLIB_H
77 #include <stdlib.h>             /* atoi() */
78 #endif
79 #include <gst/gstelement.h>
80 #include <gst/gst-i18n-plugin.h>
81 #include "gstsoupelements.h"
82 #include "gstsouphttpsrc.h"
83 #include "gstsouputils.h"
84 
85 #include <gst/tag/tag.h>
86 
87 /* this is a simple wrapper class around SoupSession; it exists in order to
88  * have a refcountable owner for the actual SoupSession + the thread it runs
89  * in and its main loop (we cannot inverse the ownership hierarchy, because
90  * the thread + loop are actually longer lived than the session)
91  *
92  * it is entirely private to this implementation
93  */
94 
95 #define GST_TYPE_SOUP_SESSION (gst_soup_session_get_type())
96 #define GST_SOUP_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SOUP_SESSION, GstSoupSession))
97 
98 GType gst_soup_session_get_type (void);
99 
100 typedef struct _GstSoupSessionClass GstSoupSessionClass;
101 
102 struct _GstSoupSession
103 {
104   GObject parent_instance;
105 
106   SoupSession *session;
107   GThread *thread;
108   GMainLoop *loop;
109 };
110 
111 struct _GstSoupSessionClass
112 {
113   GObjectClass parent_class;
114 };
115 
116 G_DEFINE_TYPE (GstSoupSession, gst_soup_session, G_TYPE_OBJECT);
117 
118 static void
gst_soup_session_init(GstSoupSession * sess)119 gst_soup_session_init (GstSoupSession * sess)
120 {
121 }
122 
123 static gboolean
_soup_session_finalize_cb(gpointer user_data)124 _soup_session_finalize_cb (gpointer user_data)
125 {
126   GstSoupSession *sess = user_data;
127 
128   g_main_loop_quit (sess->loop);
129 
130   return FALSE;
131 }
132 
133 static void
gst_soup_session_finalize(GObject * obj)134 gst_soup_session_finalize (GObject * obj)
135 {
136   GstSoupSession *sess = GST_SOUP_SESSION (obj);
137   GSource *src;
138 
139   /* handle disposing of failure cases */
140   if (!sess->loop)
141     return;
142 
143   src = g_idle_source_new ();
144 
145   g_source_set_callback (src, _soup_session_finalize_cb, sess, NULL);
146   g_source_attach (src, g_main_loop_get_context (sess->loop));
147   g_source_unref (src);
148 
149   /* finish off thread and the loop; ensure it's not from the thread */
150   g_assert (!g_main_context_is_owner (g_main_loop_get_context (sess->loop)));
151   g_thread_join (sess->thread);
152   g_main_loop_unref (sess->loop);
153 }
154 
155 static void
gst_soup_session_class_init(GstSoupSessionClass * klass)156 gst_soup_session_class_init (GstSoupSessionClass * klass)
157 {
158   GObjectClass *gclass = G_OBJECT_CLASS (klass);
159 
160   gclass->finalize = gst_soup_session_finalize;
161 }
162 
163 GST_DEBUG_CATEGORY_STATIC (souphttpsrc_debug);
164 #define GST_CAT_DEFAULT souphttpsrc_debug
165 
166 #define GST_SOUP_SESSION_CONTEXT "gst.soup.session"
167 
168 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
169     GST_PAD_SRC,
170     GST_PAD_ALWAYS,
171     GST_STATIC_CAPS_ANY);
172 
173 enum
174 {
175   PROP_0,
176   PROP_LOCATION,
177   PROP_IS_LIVE,
178   PROP_USER_AGENT,
179   PROP_AUTOMATIC_REDIRECT,
180   PROP_PROXY,
181   PROP_USER_ID,
182   PROP_USER_PW,
183   PROP_PROXY_ID,
184   PROP_PROXY_PW,
185   PROP_COOKIES,
186   PROP_IRADIO_MODE,
187   PROP_TIMEOUT,
188   PROP_EXTRA_HEADERS,
189   PROP_SOUP_LOG_LEVEL,
190   PROP_COMPRESS,
191   PROP_KEEP_ALIVE,
192   PROP_SSL_STRICT,
193   PROP_SSL_CA_FILE,
194   PROP_SSL_USE_SYSTEM_CA_FILE,
195   PROP_TLS_DATABASE,
196   PROP_RETRIES,
197   PROP_METHOD,
198   PROP_TLS_INTERACTION,
199 #ifdef OHOS_EXT_FUNC
200   // ohos.ext.func.0012
201   PROP_STATE_CHANGE,
202   PROP_EXIT_BLOCK,
203   PROP_TIME_OUT,
204 #endif
205 };
206 
207 #define DEFAULT_USER_AGENT           "GStreamer souphttpsrc " PACKAGE_VERSION " "
208 #define DEFAULT_IRADIO_MODE          TRUE
209 #define DEFAULT_SOUP_LOG_LEVEL       SOUP_LOGGER_LOG_HEADERS
210 #define DEFAULT_COMPRESS             FALSE
211 #define DEFAULT_KEEP_ALIVE           TRUE
212 #define DEFAULT_SSL_STRICT           TRUE
213 #define DEFAULT_SSL_CA_FILE          NULL
214 #define DEFAULT_SSL_USE_SYSTEM_CA_FILE TRUE
215 #define DEFAULT_TLS_DATABASE         NULL
216 #define DEFAULT_TLS_INTERACTION      NULL
217 #ifdef OHOS_EXT_FUNC
218 // ohos.ext.func.0012
219 #define DEFAULT_TIMEOUT              3
220 #define DEFAULT_RETRIES              5
221 #else
222 #define DEFAULT_TIMEOUT              15
223 #define DEFAULT_RETRIES              3
224 #endif
225 #define DEFAULT_SOUP_METHOD          NULL
226 
227 #ifdef OHOS_EXT_FUNC
228 // ohos.ext.func.0012
229 #define DEFAULT_WAIT_STEP            20000 // 20ms
230 #define SOUP_HTTP_SRC_RETRY_COUNT_ONCE 1
231 #define SOUP_HTTP_SRC_BUFFERING_TIME (-1)
232 #define POINTER_MASK  0x00FFFF00U
233 #define FAKE_POINTER(addr) ((uintptr_t) (addr) & POINTER_MASK)
234 #define SOUPHTTPSRC_INVALID_VALUE (-1)
235 #endif
236 
237 #define GROW_BLOCKSIZE_LIMIT 1
238 #define GROW_BLOCKSIZE_COUNT 1
239 #define GROW_BLOCKSIZE_FACTOR 2
240 #define REDUCE_BLOCKSIZE_LIMIT 0.20
241 #define REDUCE_BLOCKSIZE_COUNT 2
242 #define REDUCE_BLOCKSIZE_FACTOR 0.5
243 #define GROW_TIME_LIMIT (1 * GST_SECOND)
244 
245 static void gst_soup_http_src_uri_handler_init (gpointer g_iface,
246     gpointer iface_data);
247 static void gst_soup_http_src_finalize (GObject * gobject);
248 static void gst_soup_http_src_dispose (GObject * gobject);
249 
250 static void gst_soup_http_src_set_property (GObject * object, guint prop_id,
251     const GValue * value, GParamSpec * pspec);
252 static void gst_soup_http_src_get_property (GObject * object, guint prop_id,
253     GValue * value, GParamSpec * pspec);
254 
255 static GstStateChangeReturn gst_soup_http_src_change_state (GstElement *
256     element, GstStateChange transition);
257 static void gst_soup_http_src_set_context (GstElement * element,
258     GstContext * context);
259 static GstFlowReturn gst_soup_http_src_create (GstPushSrc * psrc,
260     GstBuffer ** outbuf);
261 static gboolean gst_soup_http_src_start (GstBaseSrc * bsrc);
262 static gboolean gst_soup_http_src_stop (GstBaseSrc * bsrc);
263 static gboolean gst_soup_http_src_get_size (GstBaseSrc * bsrc, guint64 * size);
264 static gboolean gst_soup_http_src_is_seekable (GstBaseSrc * bsrc);
265 static gboolean gst_soup_http_src_do_seek (GstBaseSrc * bsrc,
266     GstSegment * segment);
267 static gboolean gst_soup_http_src_query (GstBaseSrc * bsrc, GstQuery * query);
268 static gboolean gst_soup_http_src_unlock (GstBaseSrc * bsrc);
269 static gboolean gst_soup_http_src_unlock_stop (GstBaseSrc * bsrc);
270 static gboolean gst_soup_http_src_set_location (GstSoupHTTPSrc * src,
271     const gchar * uri, GError ** error);
272 static gboolean gst_soup_http_src_set_proxy (GstSoupHTTPSrc * src,
273     const gchar * uri);
274 static char *gst_soup_http_src_unicodify (const char *str);
275 static gboolean gst_soup_http_src_build_message (GstSoupHTTPSrc * src,
276     const gchar * method);
277 static gboolean gst_soup_http_src_add_range_header (GstSoupHTTPSrc * src,
278     guint64 offset, guint64 stop_offset);
279 static gboolean gst_soup_http_src_session_open (GstSoupHTTPSrc * src);
280 static void gst_soup_http_src_session_close (GstSoupHTTPSrc * src);
281 static GstFlowReturn gst_soup_http_src_parse_status (SoupMessage * msg,
282     GstSoupHTTPSrc * src);
283 static GstFlowReturn gst_soup_http_src_got_headers (GstSoupHTTPSrc * src,
284     SoupMessage * msg);
285 static void gst_soup_http_src_authenticate_cb_2 (SoupSession *,
286     SoupMessage * msg, SoupAuth * auth, gboolean retrying, gpointer);
287 static gboolean gst_soup_http_src_authenticate_cb (SoupMessage * msg,
288     SoupAuth * auth, gboolean retrying, gpointer);
289 static gboolean gst_soup_http_src_accept_certificate_cb (SoupMessage * msg,
290     GTlsCertificate * tls_certificate, GTlsCertificateFlags tls_errors,
291     gpointer user_data);
292 
293 #define gst_soup_http_src_parent_class parent_class
294 G_DEFINE_TYPE_WITH_CODE (GstSoupHTTPSrc, gst_soup_http_src, GST_TYPE_PUSH_SRC,
295     G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
296         gst_soup_http_src_uri_handler_init));
297 
298 static gboolean souphttpsrc_element_init (GstPlugin * plugin);
299 GST_ELEMENT_REGISTER_DEFINE_CUSTOM (souphttpsrc, souphttpsrc_element_init);
300 
301 static void
gst_soup_http_src_class_init(GstSoupHTTPSrcClass * klass)302 gst_soup_http_src_class_init (GstSoupHTTPSrcClass * klass)
303 {
304   GObjectClass *gobject_class;
305   GstElementClass *gstelement_class;
306   GstBaseSrcClass *gstbasesrc_class;
307   GstPushSrcClass *gstpushsrc_class;
308 
309   gobject_class = (GObjectClass *) klass;
310   gstelement_class = (GstElementClass *) klass;
311   gstbasesrc_class = (GstBaseSrcClass *) klass;
312   gstpushsrc_class = (GstPushSrcClass *) klass;
313 
314   gobject_class->set_property = gst_soup_http_src_set_property;
315   gobject_class->get_property = gst_soup_http_src_get_property;
316   gobject_class->finalize = gst_soup_http_src_finalize;
317   gobject_class->dispose = gst_soup_http_src_dispose;
318 
319   g_object_class_install_property (gobject_class,
320       PROP_LOCATION,
321       g_param_spec_string ("location", "Location",
322           "Location to read from", "",
323           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
324   g_object_class_install_property (gobject_class,
325       PROP_USER_AGENT,
326       g_param_spec_string ("user-agent", "User-Agent",
327           "Value of the User-Agent HTTP request header field",
328           DEFAULT_USER_AGENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
329   g_object_class_install_property (gobject_class,
330       PROP_AUTOMATIC_REDIRECT,
331       g_param_spec_boolean ("automatic-redirect", "automatic-redirect",
332           "Automatically follow HTTP redirects (HTTP Status Code 3xx)",
333           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
334   g_object_class_install_property (gobject_class,
335       PROP_PROXY,
336       g_param_spec_string ("proxy", "Proxy",
337           "HTTP proxy server URI", "",
338           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
339   g_object_class_install_property (gobject_class,
340       PROP_USER_ID,
341       g_param_spec_string ("user-id", "user-id",
342           "HTTP location URI user id for authentication", "",
343           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
344   g_object_class_install_property (gobject_class, PROP_USER_PW,
345       g_param_spec_string ("user-pw", "user-pw",
346           "HTTP location URI user password for authentication", "",
347           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
348   g_object_class_install_property (gobject_class, PROP_PROXY_ID,
349       g_param_spec_string ("proxy-id", "proxy-id",
350           "HTTP proxy URI user id for authentication", "",
351           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
352   g_object_class_install_property (gobject_class, PROP_PROXY_PW,
353       g_param_spec_string ("proxy-pw", "proxy-pw",
354           "HTTP proxy URI user password for authentication", "",
355           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
356   g_object_class_install_property (gobject_class, PROP_COOKIES,
357       g_param_spec_boxed ("cookies", "Cookies", "HTTP request cookies",
358           G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
359   g_object_class_install_property (gobject_class, PROP_IS_LIVE,
360       g_param_spec_boolean ("is-live", "is-live", "Act like a live source",
361           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
362   g_object_class_install_property (gobject_class, PROP_TIMEOUT,
363       g_param_spec_uint ("timeout", "timeout",
364           "Value in seconds to timeout a blocking I/O (0 = No timeout).", 0,
365           3600, DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
366   g_object_class_install_property (gobject_class, PROP_EXTRA_HEADERS,
367       g_param_spec_boxed ("extra-headers", "Extra Headers",
368           "Extra headers to append to the HTTP request",
369           GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
370   g_object_class_install_property (gobject_class, PROP_IRADIO_MODE,
371       g_param_spec_boolean ("iradio-mode", "iradio-mode",
372           "Enable internet radio mode (ask server to send shoutcast/icecast "
373           "metadata interleaved with the actual stream data)",
374           DEFAULT_IRADIO_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
375 
376 #ifdef OHOS_EXT_FUNC
377   // ohos.ext.func.0012
378   g_object_class_install_property (gobject_class, PROP_STATE_CHANGE,
379       g_param_spec_int ("state-change", "state-change from adaptive-demux",
380           "state-change from adaptive-demux", 0, (gint) (G_MAXINT32), 0,
381           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
382 
383   g_object_class_install_property (gobject_class, PROP_EXIT_BLOCK,
384       g_param_spec_int ("exit-block", "EXIT BLOCK",
385           "souphttpsrc exit block", 0, (gint) (G_MAXINT32), 0, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
386 #endif
387 
388  /**
389    * GstSoupHTTPSrc::http-log-level:
390    *
391    * If set and > 0, captures and dumps HTTP session data as
392    * log messages if log level >= GST_LEVEL_TRACE
393    *
394    * Since: 1.4
395    */
396   g_object_class_install_property (gobject_class, PROP_SOUP_LOG_LEVEL,
397       g_param_spec_enum ("http-log-level", "HTTP log level",
398           "Set log level for soup's HTTP session log",
399           _soup_logger_log_level_get_type (),
400           DEFAULT_SOUP_LOG_LEVEL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
401 
402   /**
403    * GstSoupHTTPSrc::compress:
404    *
405    * If set to %TRUE, souphttpsrc will automatically handle gzip
406    * and deflate Content-Encodings. This does not make much difference
407    * and causes more load for normal media files, but makes a real
408    * difference in size for plaintext files.
409    *
410    * Since: 1.4
411    */
412   g_object_class_install_property (gobject_class, PROP_COMPRESS,
413       g_param_spec_boolean ("compress", "Compress",
414           "Allow compressed content encodings",
415           DEFAULT_COMPRESS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
416 
417  /**
418    * GstSoupHTTPSrc::keep-alive:
419    *
420    * If set to %TRUE, souphttpsrc will keep alive connections when being
421    * set to READY state and only will close connections when connecting
422    * to a different server or when going to NULL state..
423    *
424    * Since: 1.4
425    */
426   g_object_class_install_property (gobject_class, PROP_KEEP_ALIVE,
427       g_param_spec_boolean ("keep-alive", "keep-alive",
428           "Use HTTP persistent connections", DEFAULT_KEEP_ALIVE,
429           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
430 
431  /**
432    * GstSoupHTTPSrc::ssl-strict:
433    *
434    * If set to %TRUE, souphttpsrc will reject all SSL certificates that
435    * are considered invalid.
436    *
437    * Since: 1.4
438    */
439   g_object_class_install_property (gobject_class, PROP_SSL_STRICT,
440       g_param_spec_boolean ("ssl-strict", "SSL Strict",
441           "Strict SSL certificate checking", DEFAULT_SSL_STRICT,
442           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
443 
444  /**
445    * GstSoupHTTPSrc::ssl-ca-file:
446    *
447    * A SSL anchor CA file that should be used for checking certificates
448    * instead of the system CA file.
449    *
450    * If this property is non-%NULL, #GstSoupHTTPSrc::ssl-use-system-ca-file
451    * value will be ignored.
452    *
453    * Deprecated: Use #GstSoupHTTPSrc::tls-database property instead. This
454    * property is no-op when libsoup3 is being used at runtime.
455    *
456    * Since: 1.4
457    */
458   g_object_class_install_property (gobject_class, PROP_SSL_CA_FILE,
459       g_param_spec_string ("ssl-ca-file", "SSL CA File",
460           "Location of a SSL anchor CA file to use", DEFAULT_SSL_CA_FILE,
461           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
462 
463   /**
464    * GstSoupHTTPSrc::ssl-use-system-ca-file:
465    *
466    * If set to %TRUE, souphttpsrc will use the system's CA file for
467    * checking certificates, unless #GstSoupHTTPSrc::ssl-ca-file or
468    * #GstSoupHTTPSrc::tls-database are non-%NULL.
469    *
470    * Deprecated: This property is no-op when libsoup3 is being used at runtime.
471    *
472    * Since: 1.4
473    */
474   g_object_class_install_property (gobject_class, PROP_SSL_USE_SYSTEM_CA_FILE,
475       g_param_spec_boolean ("ssl-use-system-ca-file", "Use System CA File",
476           "Use system CA file", DEFAULT_SSL_USE_SYSTEM_CA_FILE,
477           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
478 
479   /**
480    * GstSoupHTTPSrc::tls-database:
481    *
482    * TLS database with anchor certificate authorities used to validate
483    * the server certificate.
484    *
485    * If this property is non-%NULL, #GstSoupHTTPSrc::ssl-use-system-ca-file
486    * and #GstSoupHTTPSrc::ssl-ca-file values will be ignored.
487    *
488    * Since: 1.6
489    */
490   g_object_class_install_property (gobject_class, PROP_TLS_DATABASE,
491       g_param_spec_object ("tls-database", "TLS database",
492           "TLS database with anchor certificate authorities used to validate the server certificate",
493           G_TYPE_TLS_DATABASE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
494 
495   /**
496    * GstSoupHTTPSrc::tls-interaction:
497    *
498    * A #GTlsInteraction object to be used when the connection or certificate
499    * database need to interact with the user. This will be used to prompt the
500    * user for passwords or certificate where necessary.
501    *
502    * Since: 1.8
503    */
504   g_object_class_install_property (gobject_class, PROP_TLS_INTERACTION,
505       g_param_spec_object ("tls-interaction", "TLS interaction",
506           "A GTlsInteraction object to be used when the connection or certificate database need to interact with the user.",
507           G_TYPE_TLS_INTERACTION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
508 
509  /**
510    * GstSoupHTTPSrc::retries:
511    *
512    * Maximum number of retries until giving up.
513    *
514    * Since: 1.4
515    */
516   g_object_class_install_property (gobject_class, PROP_RETRIES,
517       g_param_spec_int ("retries", "Retries",
518           "Maximum number of retries until giving up (-1=infinite)", -1,
519           G_MAXINT, DEFAULT_RETRIES,
520           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
521 
522  /**
523    * GstSoupHTTPSrc::method
524    *
525    * The HTTP method to use when making a request
526    *
527    * Since: 1.6
528    */
529   g_object_class_install_property (gobject_class, PROP_METHOD,
530       g_param_spec_string ("method", "HTTP method",
531           "The HTTP method to use (GET, HEAD, OPTIONS, etc)",
532           DEFAULT_SOUP_METHOD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
533 
534   gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
535 
536   gst_element_class_set_static_metadata (gstelement_class, "HTTP client source",
537       "Source/Network",
538       "Receive data as a client over the network via HTTP using SOUP",
539       "Wouter Cloetens <wouter@mind.be>");
540   gstelement_class->change_state =
541       GST_DEBUG_FUNCPTR (gst_soup_http_src_change_state);
542   gstelement_class->set_context =
543       GST_DEBUG_FUNCPTR (gst_soup_http_src_set_context);
544 
545   gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_soup_http_src_start);
546   gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_soup_http_src_stop);
547   gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_soup_http_src_unlock);
548   gstbasesrc_class->unlock_stop =
549       GST_DEBUG_FUNCPTR (gst_soup_http_src_unlock_stop);
550   gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_soup_http_src_get_size);
551   gstbasesrc_class->is_seekable =
552       GST_DEBUG_FUNCPTR (gst_soup_http_src_is_seekable);
553   gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_soup_http_src_do_seek);
554   gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_soup_http_src_query);
555 
556   gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_soup_http_src_create);
557 }
558 
559 static void
gst_soup_http_src_reset(GstSoupHTTPSrc * src)560 gst_soup_http_src_reset (GstSoupHTTPSrc * src)
561 {
562   src->retry_count = 0;
563   src->have_size = FALSE;
564   src->got_headers = FALSE;
565   src->headers_ret = GST_FLOW_OK;
566   src->seekable = FALSE;
567   src->read_position = 0;
568   src->request_position = 0;
569   src->stop_position = -1;
570 #ifdef OHOS_OPT_COMPAT
571   // ohos.opt.compat.0017
572   /* Solve the problem of seek probability directly stuck to the end */
573   src->content_size = -1;
574 #else
575   src->content_size = 0;
576 #endif
577   src->have_body = FALSE;
578 
579   src->reduce_blocksize_count = 0;
580   src->increase_blocksize_count = 0;
581   src->last_socket_read_time = 0;
582 
583 #ifdef OHOS_EXT_FUNC
584   // ohos.ext.func.0012
585   src->wait_already = 0;
586   src->error_code = 0;
587 #endif
588 
589   g_cancellable_reset (src->cancellable);
590 
591   gst_caps_replace (&src->src_caps, NULL);
592   g_free (src->iradio_name);
593   src->iradio_name = NULL;
594   g_free (src->iradio_genre);
595   src->iradio_genre = NULL;
596   g_free (src->iradio_url);
597   src->iradio_url = NULL;
598 }
599 
600 static void
gst_soup_http_src_init(GstSoupHTTPSrc * src)601 gst_soup_http_src_init (GstSoupHTTPSrc * src)
602 {
603   const gchar *proxy;
604 
605   g_mutex_init (&src->session_mutex);
606   g_cond_init (&src->session_cond);
607 
608 #ifdef OHOS_EXT_FUNC
609   // ohos.ext.func.0012
610   g_mutex_init (&src->wait_lock);
611   g_cond_init (&src->wait_cond);
612 #endif
613   src->cancellable = g_cancellable_new ();
614   src->location = NULL;
615   src->redirection_uri = NULL;
616   src->automatic_redirect = TRUE;
617   src->user_agent = g_strdup (DEFAULT_USER_AGENT);
618   src->user_id = NULL;
619   src->user_pw = NULL;
620   src->proxy_id = NULL;
621   src->proxy_pw = NULL;
622   src->cookies = NULL;
623   src->iradio_mode = DEFAULT_IRADIO_MODE;
624   src->session = NULL;
625   src->external_session = NULL;
626   src->msg = NULL;
627   src->timeout = DEFAULT_TIMEOUT;
628   src->log_level = DEFAULT_SOUP_LOG_LEVEL;
629   src->compress = DEFAULT_COMPRESS;
630   src->keep_alive = DEFAULT_KEEP_ALIVE;
631   src->ssl_strict = DEFAULT_SSL_STRICT;
632   src->ssl_use_system_ca_file = DEFAULT_SSL_USE_SYSTEM_CA_FILE;
633   src->tls_database = DEFAULT_TLS_DATABASE;
634   src->tls_interaction = DEFAULT_TLS_INTERACTION;
635   src->max_retries = DEFAULT_RETRIES;
636   src->method = DEFAULT_SOUP_METHOD;
637   src->minimum_blocksize = gst_base_src_get_blocksize (GST_BASE_SRC_CAST (src));
638 #ifdef OHOS_EXT_FUNC
639   // ohos.ext.func.0012
640   src->last_status_code = 0;
641   src->exit_block = FALSE;
642   src->buffering_time = 0;
643   src->wait_time = DEFAULT_TIMEOUT * GST_MSECOND;
644   src->trickmode_key_units = -1;
645   src->has_sent_first_request = FALSE;
646   src->has_received_first_response = FALSE;
647   src->playerState = GST_PLAYER_STATUS_IDLE;
648 #endif
649 
650   proxy = g_getenv ("http_proxy");
651   if (!gst_soup_http_src_set_proxy (src, proxy)) {
652     GST_WARNING_OBJECT (src,
653         "The proxy in the http_proxy env var (\"%s\") cannot be parsed.",
654         proxy);
655   }
656 
657   gst_base_src_set_automatic_eos (GST_BASE_SRC (src), FALSE);
658 
659   gst_soup_http_src_reset (src);
660 }
661 
662 static void
gst_soup_http_src_dispose(GObject * gobject)663 gst_soup_http_src_dispose (GObject * gobject)
664 {
665   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (gobject);
666 
667   GST_DEBUG_OBJECT (src, "dispose");
668 
669   gst_soup_http_src_session_close (src);
670 
671   g_clear_object (&src->external_session);
672 
673   G_OBJECT_CLASS (parent_class)->dispose (gobject);
674 }
675 
676 static void
gst_soup_http_src_finalize(GObject * gobject)677 gst_soup_http_src_finalize (GObject * gobject)
678 {
679   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (gobject);
680 
681   GST_DEBUG_OBJECT (src, "finalize");
682 
683 #ifdef OHOS_EXT_FUNC
684   // ohos.ext.func.0012
685   g_mutex_clear (&src->wait_lock);
686   g_cond_clear (&src->wait_cond);
687 #endif
688   g_mutex_clear (&src->session_mutex);
689   g_cond_clear (&src->session_cond);
690   g_object_unref (src->cancellable);
691   g_free (src->location);
692   g_free (src->redirection_uri);
693   g_free (src->user_agent);
694   if (src->proxy != NULL) {
695     gst_soup_uri_free (src->proxy);
696   }
697   g_free (src->user_id);
698   g_free (src->user_pw);
699   g_free (src->proxy_id);
700   g_free (src->proxy_pw);
701   g_strfreev (src->cookies);
702 
703   if (src->extra_headers) {
704     gst_structure_free (src->extra_headers);
705     src->extra_headers = NULL;
706   }
707 
708   g_free (src->ssl_ca_file);
709 
710   if (src->tls_database)
711     g_object_unref (src->tls_database);
712   g_free (src->method);
713 
714   if (src->tls_interaction)
715     g_object_unref (src->tls_interaction);
716 
717   G_OBJECT_CLASS (parent_class)->finalize (gobject);
718 }
719 
720 static void
gst_soup_http_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)721 gst_soup_http_src_set_property (GObject * object, guint prop_id,
722     const GValue * value, GParamSpec * pspec)
723 {
724   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (object);
725 
726   switch (prop_id) {
727     case PROP_LOCATION:
728     {
729       const gchar *location;
730 
731       location = g_value_get_string (value);
732 
733       if (location == NULL) {
734         GST_WARNING ("location property cannot be NULL");
735         goto done;
736       }
737       if (!gst_soup_http_src_set_location (src, location, NULL)) {
738         GST_WARNING ("badly formatted location");
739         goto done;
740       }
741       break;
742     }
743     case PROP_USER_AGENT:
744       g_free (src->user_agent);
745       src->user_agent = g_value_dup_string (value);
746       break;
747     case PROP_IRADIO_MODE:
748       src->iradio_mode = g_value_get_boolean (value);
749       break;
750     case PROP_AUTOMATIC_REDIRECT:
751       src->automatic_redirect = g_value_get_boolean (value);
752       break;
753     case PROP_PROXY:
754     {
755       const gchar *proxy;
756 
757       proxy = g_value_get_string (value);
758       if (!gst_soup_http_src_set_proxy (src, proxy)) {
759         GST_WARNING ("badly formatted proxy URI");
760         goto done;
761       }
762       break;
763     }
764     case PROP_COOKIES:
765       g_strfreev (src->cookies);
766       src->cookies = g_strdupv (g_value_get_boxed (value));
767       break;
768     case PROP_IS_LIVE:
769       gst_base_src_set_live (GST_BASE_SRC (src), g_value_get_boolean (value));
770       break;
771     case PROP_USER_ID:
772       g_free (src->user_id);
773       src->user_id = g_value_dup_string (value);
774       break;
775     case PROP_USER_PW:
776       g_free (src->user_pw);
777       src->user_pw = g_value_dup_string (value);
778       break;
779     case PROP_PROXY_ID:
780       g_free (src->proxy_id);
781       src->proxy_id = g_value_dup_string (value);
782       break;
783     case PROP_PROXY_PW:
784       g_free (src->proxy_pw);
785       src->proxy_pw = g_value_dup_string (value);
786       break;
787     case PROP_TIMEOUT:
788       src->timeout = g_value_get_uint (value);
789       break;
790     case PROP_EXTRA_HEADERS:{
791       const GstStructure *s = gst_value_get_structure (value);
792 
793       if (src->extra_headers)
794         gst_structure_free (src->extra_headers);
795 
796       src->extra_headers = s ? gst_structure_copy (s) : NULL;
797       break;
798     }
799     case PROP_SOUP_LOG_LEVEL:
800       src->log_level = g_value_get_enum (value);
801       break;
802     case PROP_COMPRESS:
803       src->compress = g_value_get_boolean (value);
804       break;
805     case PROP_KEEP_ALIVE:
806       src->keep_alive = g_value_get_boolean (value);
807       break;
808     case PROP_SSL_STRICT:
809       src->ssl_strict = g_value_get_boolean (value);
810       break;
811     case PROP_TLS_DATABASE:
812       g_clear_object (&src->tls_database);
813       src->tls_database = g_value_dup_object (value);
814       break;
815     case PROP_TLS_INTERACTION:
816       g_clear_object (&src->tls_interaction);
817       src->tls_interaction = g_value_dup_object (value);
818       break;
819     case PROP_RETRIES:
820       src->max_retries = g_value_get_int (value);
821       break;
822     case PROP_METHOD:
823       g_free (src->method);
824       src->method = g_value_dup_string (value);
825       break;
826     case PROP_SSL_CA_FILE:
827       if (gst_soup_loader_get_api_version () == 2) {
828         g_free (src->ssl_ca_file);
829         src->ssl_ca_file = g_value_dup_string (value);
830       }
831       break;
832     case PROP_SSL_USE_SYSTEM_CA_FILE:
833       if (gst_soup_loader_get_api_version () == 2) {
834         src->ssl_use_system_ca_file = g_value_get_boolean (value);
835       }
836       break;
837 #ifdef OHOS_EXT_FUNC
838     // ohos.ext.func.0012
839     case PROP_STATE_CHANGE:
840       GST_DEBUG_OBJECT (src, "set g_value_get_int = %d ", g_value_get_int (value));
841       src->wait_time = src->timeout * GST_MSECOND;
842       src->playerState = g_value_get_int (value);
843       break;
844     case PROP_EXIT_BLOCK: {
845       gint exit_block = g_value_get_int (value);
846       if (exit_block == 1) {
847         GST_INFO_OBJECT (src, "exit_block come, broadcast wait_cond and don't wait anymore");
848         g_mutex_lock (&src->wait_lock);
849         src->exit_block = TRUE;
850         g_cond_broadcast (&src->wait_cond);
851         g_mutex_unlock (&src->wait_lock);
852       }
853       GST_DEBUG_OBJECT (src, "souphttpsrc exit_block :%d", exit_block);
854       break;
855     }
856 #endif
857     default:
858       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
859       break;
860   }
861 done:
862   return;
863 }
864 
865 static void
gst_soup_http_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)866 gst_soup_http_src_get_property (GObject * object, guint prop_id,
867     GValue * value, GParamSpec * pspec)
868 {
869   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (object);
870 
871   switch (prop_id) {
872     case PROP_LOCATION:
873       g_value_set_string (value, src->location);
874       break;
875     case PROP_USER_AGENT:
876       g_value_set_string (value, src->user_agent);
877       break;
878     case PROP_AUTOMATIC_REDIRECT:
879       g_value_set_boolean (value, src->automatic_redirect);
880       break;
881     case PROP_PROXY:
882       if (src->proxy == NULL)
883         g_value_set_static_string (value, "");
884       else {
885         char *proxy = gst_soup_uri_to_string (src->proxy);
886         g_value_set_string (value, proxy);
887         g_free (proxy);
888       }
889       break;
890     case PROP_COOKIES:
891       g_value_set_boxed (value, g_strdupv (src->cookies));
892       break;
893     case PROP_IS_LIVE:
894       g_value_set_boolean (value, gst_base_src_is_live (GST_BASE_SRC (src)));
895       break;
896     case PROP_IRADIO_MODE:
897       g_value_set_boolean (value, src->iradio_mode);
898       break;
899     case PROP_USER_ID:
900       g_value_set_string (value, src->user_id);
901       break;
902     case PROP_USER_PW:
903       g_value_set_string (value, src->user_pw);
904       break;
905     case PROP_PROXY_ID:
906       g_value_set_string (value, src->proxy_id);
907       break;
908     case PROP_PROXY_PW:
909       g_value_set_string (value, src->proxy_pw);
910       break;
911     case PROP_TIMEOUT:
912       g_value_set_uint (value, src->timeout);
913       break;
914     case PROP_EXTRA_HEADERS:
915       gst_value_set_structure (value, src->extra_headers);
916       break;
917     case PROP_SOUP_LOG_LEVEL:
918       g_value_set_enum (value, src->log_level);
919       break;
920     case PROP_COMPRESS:
921       g_value_set_boolean (value, src->compress);
922       break;
923     case PROP_KEEP_ALIVE:
924       g_value_set_boolean (value, src->keep_alive);
925       break;
926     case PROP_SSL_STRICT:
927       g_value_set_boolean (value, src->ssl_strict);
928       break;
929     case PROP_TLS_DATABASE:
930       g_value_set_object (value, src->tls_database);
931       break;
932     case PROP_TLS_INTERACTION:
933       g_value_set_object (value, src->tls_interaction);
934       break;
935     case PROP_RETRIES:
936       g_value_set_int (value, src->max_retries);
937       break;
938     case PROP_METHOD:
939       g_value_set_string (value, src->method);
940       break;
941     case PROP_SSL_CA_FILE:
942       if (gst_soup_loader_get_api_version () == 2)
943         g_value_set_string (value, src->ssl_ca_file);
944       break;
945     case PROP_SSL_USE_SYSTEM_CA_FILE:
946       if (gst_soup_loader_get_api_version () == 2)
947         g_value_set_boolean (value, src->ssl_use_system_ca_file);
948       break;
949     default:
950       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
951       break;
952   }
953 }
954 
955 static gchar *
gst_soup_http_src_unicodify(const gchar * str)956 gst_soup_http_src_unicodify (const gchar * str)
957 {
958   const gchar *env_vars[] = { "GST_ICY_TAG_ENCODING",
959     "GST_TAG_ENCODING", NULL
960   };
961 
962   return gst_tag_freeform_string_to_utf8 (str, -1, env_vars);
963 }
964 
965 static gboolean
gst_soup_http_src_add_range_header(GstSoupHTTPSrc * src,guint64 offset,guint64 stop_offset)966 gst_soup_http_src_add_range_header (GstSoupHTTPSrc * src, guint64 offset,
967     guint64 stop_offset)
968 {
969   gchar buf[64];
970   gint rc;
971   SoupMessageHeaders *request_headers =
972       _soup_message_get_request_headers (src->msg);
973 
974   _soup_message_headers_remove (request_headers, "Range");
975   if (offset || stop_offset != -1) {
976     if (stop_offset != -1) {
977       g_assert (offset != stop_offset);
978 
979       rc = g_snprintf (buf, sizeof (buf), "bytes=%" G_GUINT64_FORMAT "-%"
980           G_GUINT64_FORMAT, offset, (stop_offset > 0) ? stop_offset - 1 :
981           stop_offset);
982     } else {
983       rc = g_snprintf (buf, sizeof (buf), "bytes=%" G_GUINT64_FORMAT "-",
984           offset);
985     }
986     if (rc > sizeof (buf) || rc < 0)
987       return FALSE;
988     _soup_message_headers_append (request_headers, "Range", buf);
989   }
990   src->read_position = offset;
991   return TRUE;
992 }
993 
994 static gboolean
_append_extra_header(GQuark field_id,const GValue * value,gpointer user_data)995 _append_extra_header (GQuark field_id, const GValue * value, gpointer user_data)
996 {
997   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (user_data);
998   const gchar *field_name = g_quark_to_string (field_id);
999   gchar *field_content = NULL;
1000   SoupMessageHeaders *request_headers =
1001       _soup_message_get_request_headers (src->msg);
1002 
1003   if (G_VALUE_TYPE (value) == G_TYPE_STRING) {
1004     field_content = g_value_dup_string (value);
1005   } else {
1006     GValue dest = { 0, };
1007 
1008     g_value_init (&dest, G_TYPE_STRING);
1009     if (g_value_transform (value, &dest)) {
1010       field_content = g_value_dup_string (&dest);
1011     }
1012   }
1013 
1014   if (field_content == NULL) {
1015     GST_ERROR_OBJECT (src, "extra-headers field '%s' contains no value "
1016         "or can't be converted to a string", field_name);
1017     return FALSE;
1018   }
1019 
1020   GST_DEBUG_OBJECT (src, "Appending extra header: \"%s: %s\"", field_name,
1021       field_content);
1022   _soup_message_headers_append (request_headers, field_name, field_content);
1023 
1024   g_free (field_content);
1025 
1026   return TRUE;
1027 }
1028 
1029 static gboolean
_append_extra_headers(GQuark field_id,const GValue * value,gpointer user_data)1030 _append_extra_headers (GQuark field_id, const GValue * value,
1031     gpointer user_data)
1032 {
1033   if (G_VALUE_TYPE (value) == GST_TYPE_ARRAY) {
1034     guint n = gst_value_array_get_size (value);
1035     guint i;
1036 
1037     for (i = 0; i < n; i++) {
1038       const GValue *v = gst_value_array_get_value (value, i);
1039 
1040       if (!_append_extra_header (field_id, v, user_data))
1041         return FALSE;
1042     }
1043   } else if (G_VALUE_TYPE (value) == GST_TYPE_LIST) {
1044     guint n = gst_value_list_get_size (value);
1045     guint i;
1046 
1047     for (i = 0; i < n; i++) {
1048       const GValue *v = gst_value_list_get_value (value, i);
1049 
1050       if (!_append_extra_header (field_id, v, user_data))
1051         return FALSE;
1052     }
1053   } else {
1054     return _append_extra_header (field_id, value, user_data);
1055   }
1056 
1057   return TRUE;
1058 }
1059 
1060 
1061 static gboolean
gst_soup_http_src_add_extra_headers(GstSoupHTTPSrc * src)1062 gst_soup_http_src_add_extra_headers (GstSoupHTTPSrc * src)
1063 {
1064   if (!src->extra_headers)
1065     return TRUE;
1066 
1067   return gst_structure_foreach (src->extra_headers, _append_extra_headers, src);
1068 }
1069 
1070 static gpointer
thread_func(gpointer user_data)1071 thread_func (gpointer user_data)
1072 {
1073   GstSoupHTTPSrc *src = user_data;
1074   GstSoupSession *session = src->session;
1075   GMainContext *ctx;
1076 
1077   GST_DEBUG_OBJECT (src, "thread start");
1078 
1079   ctx = g_main_loop_get_context (session->loop);
1080 
1081   g_main_context_push_thread_default (ctx);
1082 
1083   /* We explicitly set User-Agent to NULL here and overwrite it per message
1084    * to be able to have the same session with different User-Agents per
1085    * source */
1086   session->session =
1087       _soup_session_new_with_options ("user-agent", NULL,
1088 #ifdef OHOS_EXT_FUNC
1089       "timeout", DEFAULT_TIMEOUT, "tls-interaction", src->tls_interaction,
1090 #else
1091       "timeout", src->timeout, "tls-interaction", src->tls_interaction,
1092 #endif
1093       /* Unset the limit the number of maximum allowed connections */
1094       "max-conns", src->session_is_shared ? G_MAXINT : 10,
1095       "max-conns-per-host", src->session_is_shared ? G_MAXINT : 2, NULL);
1096   g_assert (session->session);
1097 
1098   if (gst_soup_loader_get_api_version () == 3) {
1099     if (src->proxy != NULL) {
1100       GProxyResolver *proxy_resolver;
1101       char *proxy_string = gst_soup_uri_to_string (src->proxy);
1102       proxy_resolver = g_simple_proxy_resolver_new (proxy_string, NULL);
1103       g_free (proxy_string);
1104       g_object_set (src->session->session, "proxy-resolver", proxy_resolver,
1105           NULL);
1106       g_object_unref (proxy_resolver);
1107     }
1108 #if !defined(STATIC_SOUP) || STATIC_SOUP == 2
1109   } else {
1110     g_object_set (session->session, "ssl-strict", src->ssl_strict, NULL);
1111     if (src->proxy != NULL) {
1112       /* Need #if because there's no proxy->soup_uri when STATIC_SOUP == 3 */
1113       g_object_set (session->session, "proxy-uri", src->proxy->soup_uri, NULL);
1114     }
1115 #endif
1116   }
1117 
1118   gst_soup_util_log_setup (session->session, src->log_level,
1119       G_OBJECT (session));
1120   if (gst_soup_loader_get_api_version () < 3) {
1121     _soup_session_add_feature_by_type (session->session,
1122         _soup_content_decoder_get_type ());
1123   }
1124   _soup_session_add_feature_by_type (session->session,
1125       _soup_cookie_jar_get_type ());
1126 
1127   /* soup2: connect the authenticate handler for the src that spawned the
1128    * session (i.e. the first owner); other users of this session will connect
1129    * their own after fetching the external session; the callback will handle
1130    * this correctly (it checks if the message belongs to the current src
1131    * and exits early if it does not)
1132    */
1133   if (gst_soup_loader_get_api_version () < 3) {
1134     g_signal_connect (session->session, "authenticate",
1135         G_CALLBACK (gst_soup_http_src_authenticate_cb_2), src);
1136   }
1137 
1138   if (!src->session_is_shared) {
1139     if (src->tls_database)
1140       g_object_set (src->session->session, "tls-database", src->tls_database,
1141           NULL);
1142     else if (gst_soup_loader_get_api_version () == 2) {
1143       if (src->ssl_ca_file)
1144         g_object_set (src->session->session, "ssl-ca-file", src->ssl_ca_file,
1145             NULL);
1146       else
1147         g_object_set (src->session->session, "ssl-use-system-ca-file",
1148             src->ssl_use_system_ca_file, NULL);
1149     }
1150   }
1151 
1152   /* Once the main loop is running, the source element that created this
1153    * session might disappear if the session is shared with other source
1154    * elements.
1155    */
1156   src = NULL;
1157 
1158   g_main_loop_run (session->loop);
1159 
1160   /* Abort any pending operations on the session ... */
1161   _soup_session_abort (session->session);
1162   g_clear_object (&session->session);
1163 
1164   /* ... and iterate the main context until nothing is pending anymore */
1165   while (g_main_context_iteration (ctx, FALSE));
1166 
1167   g_main_context_pop_thread_default (ctx);
1168 
1169   GST_DEBUG_OBJECT (session, "thread stop");
1170 
1171   return NULL;
1172 }
1173 
1174 static gboolean
_session_ready_cb(gpointer user_data)1175 _session_ready_cb (gpointer user_data)
1176 {
1177   GstSoupHTTPSrc *src = user_data;
1178 
1179   GST_DEBUG_OBJECT (src, "thread ready");
1180 
1181   g_mutex_lock (&src->session_mutex);
1182   g_cond_signal (&src->session_cond);
1183   g_mutex_unlock (&src->session_mutex);
1184 
1185   return FALSE;
1186 }
1187 
1188 /* called with session_mutex taken */
1189 static gboolean
gst_soup_http_src_session_open(GstSoupHTTPSrc * src)1190 gst_soup_http_src_session_open (GstSoupHTTPSrc * src)
1191 {
1192   GstQuery *query;
1193   gboolean can_share;
1194 
1195   if (src->session) {
1196     GST_DEBUG_OBJECT (src, "Session is already open");
1197     return TRUE;
1198   }
1199 
1200   if (!src->location) {
1201     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (_("No URL set.")),
1202         ("Missing location property"));
1203     return FALSE;
1204   }
1205 
1206   can_share = (src->timeout == DEFAULT_TIMEOUT)
1207       && (src->cookies == NULL)
1208       && (src->ssl_strict == DEFAULT_SSL_STRICT)
1209       && (src->tls_interaction == NULL) && (src->proxy == NULL)
1210       && (src->tls_database == DEFAULT_TLS_DATABASE);
1211 
1212   if (gst_soup_loader_get_api_version () == 2)
1213     can_share = can_share && (src->ssl_ca_file == DEFAULT_SSL_CA_FILE) &&
1214         (src->ssl_use_system_ca_file == DEFAULT_SSL_USE_SYSTEM_CA_FILE);
1215 
1216   query = gst_query_new_context (GST_SOUP_SESSION_CONTEXT);
1217   if (gst_pad_peer_query (GST_BASE_SRC_PAD (src), query)) {
1218     GstContext *context;
1219 
1220     gst_query_parse_context (query, &context);
1221     gst_element_set_context (GST_ELEMENT_CAST (src), context);
1222   } else {
1223     GstMessage *message;
1224 
1225     message =
1226         gst_message_new_need_context (GST_OBJECT_CAST (src),
1227         GST_SOUP_SESSION_CONTEXT);
1228     gst_element_post_message (GST_ELEMENT_CAST (src), message);
1229   }
1230   gst_query_unref (query);
1231 
1232   GST_OBJECT_LOCK (src);
1233 
1234   src->session_is_shared = can_share;
1235 
1236   if (src->external_session && can_share) {
1237     GST_DEBUG_OBJECT (src, "Using external session %p", src->external_session);
1238     src->session = g_object_ref (src->external_session);
1239     /* for soup2, connect another authenticate handler; see thread_func */
1240     if (gst_soup_loader_get_api_version () < 3) {
1241       g_signal_connect (src->session->session, "authenticate",
1242           G_CALLBACK (gst_soup_http_src_authenticate_cb_2), src);
1243     }
1244   } else {
1245     GMainContext *ctx;
1246     GSource *source;
1247 
1248     GST_DEBUG_OBJECT (src, "Creating session (can share %d)", can_share);
1249 
1250     src->session =
1251         GST_SOUP_SESSION (g_object_new (GST_TYPE_SOUP_SESSION, NULL));
1252 
1253     GST_DEBUG_OBJECT (src, "Created session %p", src->session);
1254 
1255     ctx = g_main_context_new ();
1256 
1257     src->session->loop = g_main_loop_new (ctx, FALSE);
1258     /* now owned by the loop */
1259     g_main_context_unref (ctx);
1260 
1261     src->session->thread = g_thread_try_new ("souphttpsrc-thread",
1262         thread_func, src, NULL);
1263 
1264     if (!src->session->thread) {
1265       goto err;
1266     }
1267 
1268     source = g_idle_source_new ();
1269     g_source_set_callback (source, _session_ready_cb, src, NULL);
1270     g_source_attach (source, ctx);
1271     g_source_unref (source);
1272 
1273     GST_DEBUG_OBJECT (src, "Waiting for thread to start...");
1274     while (!g_main_loop_is_running (src->session->loop))
1275       g_cond_wait (&src->session_cond, &src->session_mutex);
1276     GST_DEBUG_OBJECT (src, "Soup thread started");
1277   }
1278 
1279   GST_OBJECT_UNLOCK (src);
1280 
1281   if (src->session_is_shared) {
1282     GstContext *context;
1283     GstMessage *message;
1284     GstStructure *s;
1285 
1286     GST_DEBUG_OBJECT (src->session, "Sharing session %p", src->session);
1287 
1288     context = gst_context_new (GST_SOUP_SESSION_CONTEXT, TRUE);
1289     s = gst_context_writable_structure (context);
1290     gst_structure_set (s, "session", GST_TYPE_SOUP_SESSION, src->session, NULL);
1291 
1292     gst_element_set_context (GST_ELEMENT_CAST (src), context);
1293     message = gst_message_new_have_context (GST_OBJECT_CAST (src), context);
1294     gst_element_post_message (GST_ELEMENT_CAST (src), message);
1295   }
1296 
1297   return TRUE;
1298 
1299 err:
1300   g_clear_object (&src->session);
1301   GST_ELEMENT_ERROR (src, LIBRARY, INIT, (NULL), ("Failed to create session"));
1302   GST_OBJECT_UNLOCK (src);
1303 
1304   return FALSE;
1305 }
1306 
1307 static gboolean
_session_close_cb(gpointer user_data)1308 _session_close_cb (gpointer user_data)
1309 {
1310   GstSoupHTTPSrc *src = user_data;
1311 
1312   if (src->msg) {
1313     gst_soup_session_cancel_message (src->session->session, src->msg,
1314         src->cancellable);
1315     g_clear_object (&src->msg);
1316   }
1317 
1318   /* there may be multiple of this callback attached to the session,
1319    * each with different data pointer; disconnect the one we are closing
1320    * the session for, leave the others alone
1321    */
1322   g_signal_handlers_disconnect_by_func (src->session->session,
1323       G_CALLBACK (gst_soup_http_src_authenticate_cb_2), src);
1324 
1325   g_mutex_lock (&src->session_mutex);
1326   g_clear_object (&src->session);
1327   g_cond_signal (&src->session_cond);
1328   g_mutex_unlock (&src->session_mutex);
1329 
1330   return FALSE;
1331 }
1332 
1333 static void
gst_soup_http_src_session_close(GstSoupHTTPSrc * src)1334 gst_soup_http_src_session_close (GstSoupHTTPSrc * src)
1335 {
1336   GSource *source;
1337   GstSoupSession *sess;
1338 
1339   GST_DEBUG_OBJECT (src, "Closing session");
1340 
1341   if (!src->session) {
1342     return;
1343   }
1344 
1345   /* ensure _session_close_cb does not deadlock us */
1346   sess = g_object_ref (src->session);
1347 
1348   source = g_idle_source_new ();
1349 
1350   g_mutex_lock (&src->session_mutex);
1351 
1352   g_source_set_callback (source, _session_close_cb, src, NULL);
1353   g_source_attach (source, g_main_loop_get_context (src->session->loop));
1354   g_source_unref (source);
1355 
1356   while (src->session)
1357     g_cond_wait (&src->session_cond, &src->session_mutex);
1358 
1359   g_mutex_unlock (&src->session_mutex);
1360 
1361   /* finally dispose of our reference from the gst thread */
1362   g_object_unref (sess);
1363 }
1364 
1365 static void
gst_soup_http_src_authenticate_cb_2(SoupSession * session,SoupMessage * msg,SoupAuth * auth,gboolean retrying,gpointer data)1366 gst_soup_http_src_authenticate_cb_2 (SoupSession * session, SoupMessage * msg,
1367     SoupAuth * auth, gboolean retrying, gpointer data)
1368 {
1369   gst_soup_http_src_authenticate_cb (msg, auth, retrying, data);
1370 }
1371 
1372 static gboolean
gst_soup_http_src_authenticate_cb(SoupMessage * msg,SoupAuth * auth,gboolean retrying,gpointer data)1373 gst_soup_http_src_authenticate_cb (SoupMessage * msg, SoupAuth * auth,
1374     gboolean retrying, gpointer data)
1375 {
1376   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (data);
1377   SoupStatus status_code;
1378 
1379   /* Might be from another user of the shared session */
1380   if (!GST_IS_SOUP_HTTP_SRC (src) || msg != src->msg)
1381     return FALSE;
1382 
1383   status_code = _soup_message_get_status (msg);
1384 
1385   if (!retrying) {
1386     /* First time authentication only, if we fail and are called again with
1387      * retry true fall through */
1388     if (status_code == SOUP_STATUS_UNAUTHORIZED) {
1389       if (src->user_id && src->user_pw) {
1390         _soup_auth_authenticate (auth, src->user_id, src->user_pw);
1391       }
1392     } else if (status_code == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
1393       if (src->proxy_id && src->proxy_pw) {
1394         _soup_auth_authenticate (auth, src->proxy_id, src->proxy_pw);
1395       }
1396     }
1397   }
1398 
1399   return FALSE;
1400 }
1401 
1402 static gboolean
gst_soup_http_src_accept_certificate_cb(SoupMessage * msg,GTlsCertificate * tls_certificate,GTlsCertificateFlags tls_errors,gpointer user_data)1403 gst_soup_http_src_accept_certificate_cb (SoupMessage * msg,
1404     GTlsCertificate * tls_certificate, GTlsCertificateFlags tls_errors,
1405     gpointer user_data)
1406 {
1407   GstSoupHTTPSrc *src = user_data;
1408 
1409   /* Might be from another user of the shared session */
1410   if (!GST_IS_SOUP_HTTP_SRC (src) || msg != src->msg)
1411     return FALSE;
1412 
1413   /* Accept invalid certificates */
1414   if (!src->ssl_strict)
1415     return TRUE;
1416 
1417   return FALSE;
1418 }
1419 
1420 static void
insert_http_header(const gchar * name,const gchar * value,gpointer user_data)1421 insert_http_header (const gchar * name, const gchar * value, gpointer user_data)
1422 {
1423   GstStructure *headers = user_data;
1424   const GValue *gv;
1425 
1426   if (!g_utf8_validate (name, -1, NULL) || !g_utf8_validate (value, -1, NULL))
1427     return;
1428 
1429   gv = gst_structure_get_value (headers, name);
1430   if (gv && GST_VALUE_HOLDS_ARRAY (gv)) {
1431     GValue v = G_VALUE_INIT;
1432 
1433     g_value_init (&v, G_TYPE_STRING);
1434     g_value_set_string (&v, value);
1435     gst_value_array_append_value ((GValue *) gv, &v);
1436     g_value_unset (&v);
1437   } else if (gv && G_VALUE_HOLDS_STRING (gv)) {
1438     GValue arr = G_VALUE_INIT;
1439     GValue v = G_VALUE_INIT;
1440     const gchar *old_value = g_value_get_string (gv);
1441 
1442     g_value_init (&arr, GST_TYPE_ARRAY);
1443     g_value_init (&v, G_TYPE_STRING);
1444     g_value_set_string (&v, old_value);
1445     gst_value_array_append_value (&arr, &v);
1446     g_value_set_string (&v, value);
1447     gst_value_array_append_value (&arr, &v);
1448 
1449     gst_structure_set_value (headers, name, &arr);
1450     g_value_unset (&v);
1451     g_value_unset (&arr);
1452   } else {
1453     gst_structure_set (headers, name, G_TYPE_STRING, value, NULL);
1454   }
1455 }
1456 
1457 static GstFlowReturn
gst_soup_http_src_got_headers(GstSoupHTTPSrc * src,SoupMessage * msg)1458 gst_soup_http_src_got_headers (GstSoupHTTPSrc * src, SoupMessage * msg)
1459 {
1460   const char *value;
1461   GstTagList *tag_list;
1462   GstBaseSrc *basesrc;
1463   guint64 newsize;
1464   GHashTable *params = NULL;
1465   GstEvent *http_headers_event;
1466   GstStructure *http_headers, *headers;
1467   const gchar *accept_ranges;
1468   SoupMessageHeaders *request_headers = _soup_message_get_request_headers (msg);
1469   SoupMessageHeaders *response_headers =
1470       _soup_message_get_response_headers (msg);
1471   SoupStatus status_code = _soup_message_get_status (msg);
1472 
1473 #ifdef OHOS_EXT_FUNC
1474   // ohos.ext.func.0012
1475   if (src->has_received_first_response == FALSE) {
1476     src->has_received_first_response = TRUE;
1477   }
1478 
1479   GST_INFO_OBJECT (src, "got headers, status code %d", status_code);
1480   if ((src->last_status_code != status_code) && (status_code >= SOUP_STATUS_MULTIPLE_CHOICES) &&
1481       (status_code <= SOUP_STATUS_NOT_EXTENDED)) {
1482     src->last_status_code = status_code;
1483   }
1484 #else
1485   GST_INFO_OBJECT (src, "got headers");
1486 #endif
1487 
1488   if (status_code == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED &&
1489       src->proxy_id && src->proxy_pw) {
1490     /* wait for authenticate callback */
1491     return GST_FLOW_OK;
1492   }
1493 
1494   http_headers = gst_structure_new_empty ("http-headers");
1495   gst_structure_set (http_headers, "uri", G_TYPE_STRING, src->location,
1496       "http-status-code", G_TYPE_UINT, status_code, NULL);
1497   if (src->redirection_uri)
1498     gst_structure_set (http_headers, "redirection-uri", G_TYPE_STRING,
1499         src->redirection_uri, NULL);
1500   headers = gst_structure_new_empty ("request-headers");
1501   _soup_message_headers_foreach (request_headers, insert_http_header, headers);
1502   gst_structure_set (http_headers, "request-headers", GST_TYPE_STRUCTURE,
1503       headers, NULL);
1504   gst_structure_free (headers);
1505   headers = gst_structure_new_empty ("response-headers");
1506   _soup_message_headers_foreach (response_headers, insert_http_header, headers);
1507   gst_structure_set (http_headers, "response-headers", GST_TYPE_STRUCTURE,
1508       headers, NULL);
1509   gst_structure_free (headers);
1510 
1511   gst_element_post_message (GST_ELEMENT_CAST (src),
1512       gst_message_new_element (GST_OBJECT_CAST (src),
1513           gst_structure_copy (http_headers)));
1514 
1515   if (status_code == SOUP_STATUS_UNAUTHORIZED) {
1516     /* force an error */
1517     gst_structure_free (http_headers);
1518     return gst_soup_http_src_parse_status (msg, src);
1519   }
1520 
1521   src->got_headers = TRUE;
1522 
1523   http_headers_event =
1524       gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY, http_headers);
1525   gst_event_replace (&src->http_headers_event, http_headers_event);
1526   gst_event_unref (http_headers_event);
1527 
1528   /* Parse Content-Length. */
1529   if (SOUP_STATUS_IS_SUCCESSFUL (status_code) &&
1530       (_soup_message_headers_get_encoding (response_headers) ==
1531           SOUP_ENCODING_CONTENT_LENGTH)) {
1532     newsize = src->request_position +
1533         _soup_message_headers_get_content_length (response_headers);
1534     if (!src->have_size || (src->content_size != newsize)) {
1535       src->content_size = newsize;
1536       src->have_size = TRUE;
1537       src->seekable = TRUE;
1538       GST_DEBUG_OBJECT (src, "size = %" G_GUINT64_FORMAT, src->content_size);
1539 
1540       basesrc = GST_BASE_SRC_CAST (src);
1541       basesrc->segment.duration = src->content_size;
1542       gst_element_post_message (GST_ELEMENT (src),
1543           gst_message_new_duration_changed (GST_OBJECT (src)));
1544     }
1545   }
1546 
1547   /* If the server reports Accept-Ranges: none we don't have to try
1548    * doing range requests at all
1549    */
1550   if ((accept_ranges =
1551           _soup_message_headers_get_one (response_headers, "Accept-Ranges"))) {
1552     if (g_ascii_strcasecmp (accept_ranges, "none") == 0)
1553       src->seekable = FALSE;
1554   }
1555 
1556   /* Icecast stuff */
1557   tag_list = gst_tag_list_new_empty ();
1558 
1559   if ((value =
1560           _soup_message_headers_get_one (response_headers,
1561               "icy-metaint")) != NULL) {
1562     gint icy_metaint;
1563 
1564     if (g_utf8_validate (value, -1, NULL)) {
1565       icy_metaint = atoi (value);
1566 
1567       GST_DEBUG_OBJECT (src, "icy-metaint: %s (parsed: %d)", value,
1568           icy_metaint);
1569       if (icy_metaint > 0) {
1570         if (src->src_caps)
1571           gst_caps_unref (src->src_caps);
1572 
1573         src->src_caps = gst_caps_new_simple ("application/x-icy",
1574             "metadata-interval", G_TYPE_INT, icy_metaint, NULL);
1575 
1576         gst_base_src_set_caps (GST_BASE_SRC (src), src->src_caps);
1577       }
1578     }
1579   }
1580   if ((value =
1581           _soup_message_headers_get_content_type (response_headers,
1582               &params)) != NULL) {
1583     if (!g_utf8_validate (value, -1, NULL)) {
1584       GST_WARNING_OBJECT (src, "Content-Type is invalid UTF-8");
1585     } else if (g_ascii_strcasecmp (value, "audio/L16") == 0) {
1586       gint channels = 2;
1587       gint rate = 44100;
1588       char *param;
1589 
1590       GST_DEBUG_OBJECT (src, "Content-Type: %s", value);
1591 
1592       if (src->src_caps) {
1593         gst_caps_unref (src->src_caps);
1594         src->src_caps = NULL;
1595       }
1596 
1597       param = g_hash_table_lookup (params, "channels");
1598       if (param != NULL) {
1599         guint64 val = g_ascii_strtoull (param, NULL, 10);
1600         if (val < 64)
1601           channels = val;
1602         else
1603           channels = 0;
1604       }
1605 
1606       param = g_hash_table_lookup (params, "rate");
1607       if (param != NULL) {
1608         guint64 val = g_ascii_strtoull (param, NULL, 10);
1609         if (val < G_MAXINT)
1610           rate = val;
1611         else
1612           rate = 0;
1613       }
1614 
1615       if (rate > 0 && channels > 0) {
1616         src->src_caps = gst_caps_new_simple ("audio/x-unaligned-raw",
1617             "format", G_TYPE_STRING, "S16BE",
1618             "layout", G_TYPE_STRING, "interleaved",
1619             "channels", G_TYPE_INT, channels, "rate", G_TYPE_INT, rate, NULL);
1620 
1621         gst_base_src_set_caps (GST_BASE_SRC (src), src->src_caps);
1622       }
1623     } else {
1624       GST_DEBUG_OBJECT (src, "Content-Type: %s", value);
1625 
1626       /* Set the Content-Type field on the caps */
1627       if (src->src_caps) {
1628         src->src_caps = gst_caps_make_writable (src->src_caps);
1629         gst_caps_set_simple (src->src_caps, "content-type", G_TYPE_STRING,
1630             value, NULL);
1631         gst_base_src_set_caps (GST_BASE_SRC (src), src->src_caps);
1632       }
1633     }
1634   }
1635 
1636   if (params != NULL)
1637     g_hash_table_destroy (params);
1638 
1639   if ((value =
1640           _soup_message_headers_get_one (response_headers,
1641               "icy-name")) != NULL) {
1642     if (g_utf8_validate (value, -1, NULL)) {
1643       g_free (src->iradio_name);
1644       src->iradio_name = gst_soup_http_src_unicodify (value);
1645       if (src->iradio_name) {
1646         gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION,
1647             src->iradio_name, NULL);
1648       }
1649     }
1650   }
1651   if ((value =
1652           _soup_message_headers_get_one (response_headers,
1653               "icy-genre")) != NULL) {
1654     if (g_utf8_validate (value, -1, NULL)) {
1655       g_free (src->iradio_genre);
1656       src->iradio_genre = gst_soup_http_src_unicodify (value);
1657       if (src->iradio_genre) {
1658         gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE,
1659             src->iradio_genre, NULL);
1660       }
1661     }
1662   }
1663   if ((value = _soup_message_headers_get_one (response_headers, "icy-url"))
1664       != NULL) {
1665     if (g_utf8_validate (value, -1, NULL)) {
1666       g_free (src->iradio_url);
1667       src->iradio_url = gst_soup_http_src_unicodify (value);
1668       if (src->iradio_url) {
1669         gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_LOCATION,
1670             src->iradio_url, NULL);
1671       }
1672     }
1673   }
1674   if (!gst_tag_list_is_empty (tag_list)) {
1675     GST_DEBUG_OBJECT (src,
1676         "calling gst_element_found_tags with %" GST_PTR_FORMAT, tag_list);
1677     gst_pad_push_event (GST_BASE_SRC_PAD (src), gst_event_new_tag (tag_list));
1678   } else {
1679     gst_tag_list_unref (tag_list);
1680   }
1681 
1682   /* Handle HTTP errors. */
1683   return gst_soup_http_src_parse_status (msg, src);
1684 }
1685 
1686 static GstBuffer *
gst_soup_http_src_alloc_buffer(GstSoupHTTPSrc * src)1687 gst_soup_http_src_alloc_buffer (GstSoupHTTPSrc * src)
1688 {
1689   GstBaseSrc *basesrc = GST_BASE_SRC_CAST (src);
1690   GstFlowReturn rc;
1691   GstBuffer *gstbuf;
1692 
1693   rc = GST_BASE_SRC_CLASS (parent_class)->alloc (basesrc, -1,
1694       basesrc->blocksize, &gstbuf);
1695   if (G_UNLIKELY (rc != GST_FLOW_OK)) {
1696     return NULL;
1697   }
1698 
1699   return gstbuf;
1700 }
1701 
1702 #define SOUP_HTTP_SRC_ERROR(src,soup_msg,cat,code,error_message)     \
1703   do { \
1704     GST_ELEMENT_ERROR_WITH_DETAILS ((src), cat, code, ("%s", error_message), \
1705         ("%s (%d), URL: %s, Redirect to: %s", _soup_message_get_reason_phrase (soup_msg), \
1706             _soup_message_get_status (soup_msg), (src)->location, GST_STR_NULL ((src)->redirection_uri)), \
1707             ("http-status-code", G_TYPE_UINT, _soup_message_get_status (soup_msg), \
1708              "http-redirect-uri", G_TYPE_STRING, GST_STR_NULL ((src)->redirection_uri), NULL)); \
1709   } while(0)
1710 
1711 #ifdef OHOS_EXT_FUNC
1712 // ohos.ext.func.0012
1713 #define SOUP_HTTP_SRC_ERROR_HTTP_STANDARD_ERRORS(src, soup_msg, http_error_code, cat, code, error_message) \
1714 G_STMT_START { \
1715   GST_ELEMENT_ERROR_WITH_DETAILS ((src), cat, code, ("%s", error_message), \
1716       ("%s (%d), URL: %s, Redirect to: %s", _soup_message_get_reason_phrase (soup_msg), \
1717            http_error_code, (src)->location, GST_STR_NULL ((src)->redirection_uri)), \
1718       ("http-status-code", G_TYPE_UINT, http_error_code, \
1719           "http-redirect-uri", G_TYPE_STRING, GST_STR_NULL ((src)->redirection_uri), NULL)); \
1720 } G_STMT_END
1721 
1722 static void
wait_for_connect_exit(GstSoupHTTPSrc * src,SoupMessage * msg)1723 wait_for_connect_exit (GstSoupHTTPSrc *src, SoupMessage *msg)
1724 {
1725   g_mutex_lock (&src->wait_lock);
1726   if (!src->exit_block) {
1727     SOUP_HTTP_SRC_ERROR_HTTP_STANDARD_ERRORS (src, msg, SOUP_STATUS_REQUEST_TIMEOUT, RESOURCE, TIME_OUT,
1728         _("Could not establish connection to server."));
1729     src->headers_ret = GST_FLOW_CUSTOM_SUCCESS;
1730   } else {
1731     /* if return GST_FLOW_ERROR, basesrc will report internal stream error, which we should not */
1732     GST_INFO_OBJECT (src, "now it's reset, don't post timeout msg!, return EOS");
1733     src->headers_ret = GST_FLOW_EOS;
1734   }
1735 
1736   src->wait_time = -1;
1737   src->wait_already = 0;
1738   src->exit_block = FALSE;
1739   g_mutex_unlock (&src->wait_lock);
1740 }
1741 
1742 static gint
proc_buffering_time(GstSoupHTTPSrc * src,gint64 timeout)1743 proc_buffering_time (GstSoupHTTPSrc *src, gint64 timeout)
1744 {
1745   g_mutex_lock (&src->wait_lock);
1746   if ((timeout <= 0) || src->exit_block) {
1747     GST_INFO_OBJECT (src, "wait time out, or should quit immediately"
1748         " timeout:%"G_GUINT64_FORMAT", src->exit_block:%d", timeout, src->exit_block);
1749     g_mutex_unlock (&src->wait_lock);
1750     return SOUPHTTPSRC_INVALID_VALUE;
1751   }
1752 
1753   if (src->retry_count == SOUP_HTTP_SRC_RETRY_COUNT_ONCE) {
1754     src->buffering_time = g_get_monotonic_time ();
1755     GST_INFO_OBJECT (src, "can not connect before start,begin to time:%"G_GUINT64_FORMAT" ", src->buffering_time);
1756   } else if ((src->buffering_time == SOUP_HTTP_SRC_BUFFERING_TIME) && (src->trickmode_key_units != 1)) {
1757     GST_INFO_OBJECT (src, "souphttpsrc not in buffering, sleep 20ms wait for buffering, "
1758         "src->retry_count:%d, src->buffering_time:%"G_GUINT64_FORMAT"\n", src->retry_count, src->buffering_time);
1759     if (src->exit_block || g_cond_wait_until (&src->wait_cond, &src->wait_lock,
1760         (g_get_monotonic_time () + DEFAULT_WAIT_STEP))) {
1761       g_mutex_unlock (&src->wait_lock);
1762       return SOUPHTTPSRC_INVALID_VALUE;
1763     }
1764     g_mutex_unlock (&src->wait_lock);
1765     src->retry_count = 0;
1766     src->headers_ret = GST_FLOW_CUSTOM_ERROR;
1767     return 0;
1768   } else if ((src->buffering_time == SOUP_HTTP_SRC_BUFFERING_TIME) && (src->trickmode_key_units == 1)) {
1769     src->buffering_time = g_get_monotonic_time ();
1770     GST_INFO_OBJECT (src, "it's trickmode key units,begin to time:%"G_GUINT64_FORMAT" ", src->buffering_time);
1771     src->retry_count = 1;
1772   }
1773   g_mutex_unlock (&src->wait_lock);
1774   return 1;
1775 }
1776 
1777 /* timeout : us */
1778 static void
wait_for_connect(GstSoupHTTPSrc * src,SoupMessage * msg,gint64 timeout)1779 wait_for_connect (GstSoupHTTPSrc *src, SoupMessage *msg, gint64 timeout)
1780 {
1781   if ((src == NULL) || (msg == NULL)) {
1782     return;
1783   }
1784 
1785   gint ret = proc_buffering_time (src, timeout);
1786   if (ret == 0) {
1787     return;
1788   } else if (ret == SOUPHTTPSRC_INVALID_VALUE) {
1789     wait_for_connect_exit (src, msg);
1790     return;
1791   }
1792 
1793   gint64 time_diff_us = (g_get_monotonic_time () - src->buffering_time);
1794   if (src->retry_count == SOUP_HTTP_SRC_RETRY_COUNT_ONCE) {
1795     src->max_retries = (timeout - time_diff_us) / DEFAULT_WAIT_STEP + 1;
1796   }
1797 
1798   GST_DEBUG_OBJECT (src, "max_retries:%d, timeout:%"G_GUINT64_FORMAT" time_diff_us = %"G_GUINT64_FORMAT" %"G_GUINT64_FORMAT" %"G_GUINT64_FORMAT"",
1799     src->max_retries, timeout, time_diff_us, g_get_monotonic_time (), src->buffering_time);
1800 
1801   if (time_diff_us >= timeout) {
1802     GST_ERROR_OBJECT (src, "now wait time is %"G_GUINT64_FORMAT" us, timeout:%"G_GUINT64_FORMAT", and quit", time_diff_us, timeout);
1803     wait_for_connect_exit (src, msg);
1804     return;
1805   }
1806 
1807   GST_INFO_OBJECT (src, "retry_count:%d, and sleep :%d us, timeout:%"G_GUINT64_FORMAT"", src->retry_count, DEFAULT_WAIT_STEP, timeout);
1808   g_mutex_lock (&src->wait_lock);
1809   if (src->exit_block || g_cond_wait_until (&src->wait_cond, &src->wait_lock,
1810       (g_get_monotonic_time () + DEFAULT_WAIT_STEP))) {
1811     g_mutex_unlock (&src->wait_lock);
1812     wait_for_connect_exit (src, msg);
1813     return;
1814   }
1815   g_mutex_unlock (&src->wait_lock);
1816   src->headers_ret = GST_FLOW_CUSTOM_ERROR;
1817   return;
1818 }
1819 
1820 static GstFlowReturn
gst_soup_http_src_handle_connect_fail_ohos(SoupMessage * msg,GstSoupHTTPSrc * src)1821 gst_soup_http_src_handle_connect_fail_ohos (SoupMessage * msg, GstSoupHTTPSrc * src)
1822 {
1823   /* ohos.ext.func.0012
1824    The network is disconnected and reconnected. The time-out is 3 seconds after starting broadcasting,
1825    and the time-out is 15 seconds after interruption during broadcasting
1826    */
1827   if (!src->exit_block) {
1828       if (src->playerState == GST_PLAYER_STATUS_PAUSED || src->playerState == GST_PLAYER_STATUS_PLAYING) {
1829         src->retry_count = 0;
1830         return GST_FLOW_CUSTOM_ERROR;
1831     }
1832   }
1833 
1834   if (_soup_message_get_status (msg) == SOUP_STATUS_IO_ERROR && src->playerState != GST_PLAYER_STATUS_BUFFERING) {
1835     wait_for_connect (src, msg, 0);
1836   } else {
1837     wait_for_connect (src, msg, (gint64) src->wait_time - (gint64) src->wait_already);
1838   }
1839 
1840   if (src->max_retries == -1 || src->retry_count < src->max_retries)
1841     return GST_FLOW_CUSTOM_ERROR;
1842 
1843   return GST_FLOW_OK;
1844 }
1845 
1846 static GstFlowReturn
gst_soup_http_src_parse_status_ohos(SoupMessage * msg,GstSoupHTTPSrc * src)1847 gst_soup_http_src_parse_status_ohos (SoupMessage * msg, GstSoupHTTPSrc * src)
1848 {
1849   SoupStatus status_code = _soup_message_get_status (msg);
1850   GstFlowReturn ret = GST_FLOW_ERROR;
1851 
1852   switch (status_code) {
1853     case SOUP_STATUS_CANT_RESOLVE:
1854     case SOUP_STATUS_CANT_RESOLVE_PROXY: {
1855       if ((ret = gst_soup_http_src_handle_connect_fail_ohos(msg, src)) != GST_FLOW_OK) {
1856         break;
1857       }
1858       SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, NOT_FOUND, _("Could not resolve server name."));
1859       ret = GST_FLOW_ERROR;
1860       break;
1861     }
1862     case SOUP_STATUS_CANT_CONNECT:
1863     case SOUP_STATUS_CANT_CONNECT_PROXY: {
1864       if ((ret = gst_soup_http_src_handle_connect_fail_ohos(msg, src)) != GST_FLOW_OK) {
1865         break;
1866       }
1867       SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, OPEN_READ, _("Could not establish connection to server."));
1868       ret = GST_FLOW_ERROR;
1869       break;
1870     }
1871     case SOUP_STATUS_IO_ERROR: {
1872       if ((ret = gst_soup_http_src_handle_connect_fail_ohos(msg, src)) != GST_FLOW_OK) {
1873         break;
1874       }
1875       SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, READ,
1876           _("A network error occurred, or the server closed the connection unexpectedly."));
1877       ret = GST_FLOW_ERROR;
1878       break;
1879     }
1880     case SOUP_STATUS_MALFORMED:
1881       SOUP_HTTP_SRC_ERROR_HTTP_STANDARD_ERRORS (src, msg, SOUP_STATUS_BAD_REQUEST, RESOURCE, READ,
1882           _("Server sent bad data."));
1883       break;
1884     default:
1885       // return ok means not care, need the original gstsouphttpsrc code to process.
1886       ret = GST_FLOW_OK;
1887       break;
1888   }
1889 
1890   return ret;
1891 }
1892 #endif
1893 
1894 static GstFlowReturn
gst_soup_http_src_parse_status(SoupMessage * msg,GstSoupHTTPSrc * src)1895 gst_soup_http_src_parse_status (SoupMessage * msg, GstSoupHTTPSrc * src)
1896 {
1897   SoupStatus status_code = _soup_message_get_status (msg);
1898   if (_soup_message_get_method (msg) == SOUP_METHOD_HEAD) {
1899     if (!SOUP_STATUS_IS_SUCCESSFUL (status_code))
1900       GST_DEBUG_OBJECT (src, "Ignoring error %d during HEAD request",
1901           status_code);
1902     return GST_FLOW_OK;
1903   }
1904 
1905   /* SOUP_STATUS_IS_TRANSPORT_ERROR was replaced with GError in libsoup-3.0 */
1906 #if !defined(STATIC_SOUP) || STATIC_SOUP == 2
1907   if (SOUP_STATUS_IS_TRANSPORT_ERROR (status_code)) {
1908 #ifdef OHOS_EXT_FUNC
1909     // ohos.ext.func.0012
1910     GstFlowReturn ret = gst_soup_http_src_parse_status_ohos (msg, src);
1911     if (ret != GST_FLOW_OK) {
1912       return ret;
1913     }
1914 #endif
1915     switch (status_code) {
1916       case SOUP_STATUS_CANT_RESOLVE:
1917       case SOUP_STATUS_CANT_RESOLVE_PROXY:
1918         SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, NOT_FOUND,
1919             _("Could not resolve server name."));
1920         return GST_FLOW_ERROR;
1921       case SOUP_STATUS_CANT_CONNECT:
1922       case SOUP_STATUS_CANT_CONNECT_PROXY:
1923         SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, OPEN_READ,
1924             _("Could not establish connection to server."));
1925         return GST_FLOW_ERROR;
1926       case SOUP_STATUS_SSL_FAILED:
1927         SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, OPEN_READ,
1928             _("Secure connection setup failed."));
1929         return GST_FLOW_ERROR;
1930       case SOUP_STATUS_IO_ERROR:
1931         if (src->max_retries == -1 || src->retry_count < src->max_retries)
1932           return GST_FLOW_CUSTOM_ERROR;
1933         SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, READ,
1934             _("A network error occurred, or the server closed the connection "
1935                 "unexpectedly."));
1936         return GST_FLOW_ERROR;
1937       case SOUP_STATUS_MALFORMED:
1938         SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, READ,
1939             _("Server sent bad data."));
1940         return GST_FLOW_ERROR;
1941       case SOUP_STATUS_CANCELLED:
1942         /* No error message when interrupted by program. */
1943         break;
1944       default:
1945         break;
1946     }
1947     return GST_FLOW_OK;
1948   }
1949 #endif
1950 
1951   if (SOUP_STATUS_IS_CLIENT_ERROR (status_code) ||
1952       SOUP_STATUS_IS_REDIRECTION (status_code) ||
1953       SOUP_STATUS_IS_SERVER_ERROR (status_code)) {
1954     const gchar *reason_phrase;
1955 
1956     reason_phrase = _soup_message_get_reason_phrase (msg);
1957     if (reason_phrase && !g_utf8_validate (reason_phrase, -1, NULL)) {
1958       GST_ERROR_OBJECT (src, "Invalid UTF-8 in reason");
1959       reason_phrase = "(invalid)";
1960     }
1961 
1962     /* Report HTTP error. */
1963 
1964     /* when content_size is unknown and we have just finished receiving
1965      * a body message, requests that go beyond the content limits will result
1966      * in an error. Here we convert those to EOS */
1967     if (status_code == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE &&
1968         src->have_body && (!src->have_size ||
1969             (src->request_position >= src->content_size))) {
1970       GST_DEBUG_OBJECT (src, "Requested range out of limits and received full "
1971           "body, returning EOS");
1972       return GST_FLOW_EOS;
1973     }
1974 
1975     /* FIXME: reason_phrase is not translated and not suitable for user
1976      * error dialog according to libsoup documentation.
1977      */
1978     if (status_code == SOUP_STATUS_NOT_FOUND) {
1979       SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, NOT_FOUND, (reason_phrase));
1980 #ifdef OHOS_EXT_FUNC
1981       // ohos.ext.func.0012
1982       return GST_FLOW_CUSTOM_SUCCESS;
1983 #endif
1984     } else if (status_code == SOUP_STATUS_UNAUTHORIZED
1985         || status_code == SOUP_STATUS_PAYMENT_REQUIRED
1986         || status_code == SOUP_STATUS_FORBIDDEN
1987         || status_code == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
1988       SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, NOT_AUTHORIZED, (reason_phrase));
1989 #ifdef OHOS_EXT_FUNC
1990       // ohos.ext.func.0012
1991       return GST_FLOW_EOS;
1992 #endif
1993     } else {
1994       SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, OPEN_READ, (reason_phrase));
1995 #ifdef OHOS_EXT_FUNC
1996       // ohos.ext.func.0012
1997       return GST_FLOW_ERROR;
1998 #endif
1999     }
2000     return GST_FLOW_ERROR;
2001   }
2002 
2003   return GST_FLOW_OK;
2004 }
2005 
2006 static void
gst_soup_http_src_restarted_cb(SoupMessage * msg,GstSoupHTTPSrc * src)2007 gst_soup_http_src_restarted_cb (SoupMessage * msg, GstSoupHTTPSrc * src)
2008 {
2009   SoupStatus status = _soup_message_get_status (msg);
2010 
2011   if (!SOUP_STATUS_IS_REDIRECTION (status))
2012     return;
2013 
2014   src->redirection_uri = gst_soup_message_uri_to_string (msg);
2015   src->redirection_permanent = (status == SOUP_STATUS_MOVED_PERMANENTLY);
2016 
2017   GST_DEBUG_OBJECT (src, "%u redirect to \"%s\" (permanent %d)",
2018       status, src->redirection_uri, src->redirection_permanent);
2019 }
2020 
2021 static gboolean
gst_soup_http_src_build_message(GstSoupHTTPSrc * src,const gchar * method)2022 gst_soup_http_src_build_message (GstSoupHTTPSrc * src, const gchar * method)
2023 {
2024   SoupMessageHeaders *request_headers;
2025 
2026   g_return_val_if_fail (src->msg == NULL, FALSE);
2027 
2028   src->msg = _soup_message_new (method, src->location);
2029   if (!src->msg) {
2030     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
2031         ("Error parsing URL."), ("URL: %s", src->location));
2032     return FALSE;
2033   }
2034 
2035   request_headers = _soup_message_get_request_headers (src->msg);
2036 
2037   /* Duplicating the defaults of libsoup here. We don't want to set a
2038    * User-Agent in the session as each source might have its own User-Agent
2039    * set */
2040   if (!src->user_agent || !*src->user_agent) {
2041     gchar *user_agent =
2042         g_strdup_printf ("libsoup/%u.%u.%u", _soup_get_major_version (),
2043         _soup_get_minor_version (), _soup_get_micro_version ());
2044     _soup_message_headers_append (request_headers, "User-Agent", user_agent);
2045     g_free (user_agent);
2046   } else if (g_str_has_suffix (src->user_agent, " ")) {
2047     gchar *user_agent = g_strdup_printf ("%slibsoup/%u.%u.%u", src->user_agent,
2048         _soup_get_major_version (),
2049         _soup_get_minor_version (), _soup_get_micro_version ());
2050     _soup_message_headers_append (request_headers, "User-Agent", user_agent);
2051     g_free (user_agent);
2052   } else {
2053     _soup_message_headers_append (request_headers, "User-Agent",
2054         src->user_agent);
2055   }
2056 
2057   if (!src->keep_alive) {
2058     _soup_message_headers_append (request_headers, "Connection", "close");
2059   }
2060   if (src->iradio_mode) {
2061     _soup_message_headers_append (request_headers, "icy-metadata", "1");
2062   }
2063   if (src->cookies) {
2064     gchar **cookie;
2065 
2066     for (cookie = src->cookies; *cookie != NULL; cookie++) {
2067       _soup_message_headers_append (request_headers, "Cookie", *cookie);
2068     }
2069 
2070     _soup_message_disable_feature (src->msg, _soup_cookie_jar_get_type ());
2071   }
2072 
2073   if (!src->compress) {
2074     _soup_message_headers_append (_soup_message_get_request_headers (src->msg),
2075         "Accept-Encoding", "identity");
2076   }
2077 
2078   if (gst_soup_loader_get_api_version () == 3) {
2079     g_signal_connect (src->msg, "accept-certificate",
2080         G_CALLBACK (gst_soup_http_src_accept_certificate_cb), src);
2081     g_signal_connect (src->msg, "authenticate",
2082         G_CALLBACK (gst_soup_http_src_authenticate_cb), src);
2083   }
2084 
2085   {
2086     SoupMessageFlags flags =
2087         src->automatic_redirect ? 0 : SOUP_MESSAGE_NO_REDIRECT;
2088 
2089     /* SOUP_MESSAGE_OVERWRITE_CHUNKS is gone in libsoup-3.0, and
2090      * soup_message_body_set_accumulate() requires SoupMessageBody, which
2091      * can only be fetched from SoupServerMessage, not SoupMessage */
2092 #if !defined(STATIC_SOUP) || STATIC_SOUP == 2
2093     if (gst_soup_loader_get_api_version () == 2)
2094       flags |= SOUP_MESSAGE_OVERWRITE_CHUNKS;
2095 #endif
2096 
2097     _soup_message_set_flags (src->msg, flags);
2098   }
2099 
2100   if (src->automatic_redirect) {
2101     g_signal_connect (src->msg, "restarted",
2102         G_CALLBACK (gst_soup_http_src_restarted_cb), src);
2103   }
2104 
2105   gst_soup_http_src_add_range_header (src, src->request_position,
2106       src->stop_position);
2107 
2108   gst_soup_http_src_add_extra_headers (src);
2109 
2110   return TRUE;
2111 }
2112 
2113 #ifdef OHOS_EXT_FUNC
2114 // ohos.ext.func.0012
2115 static gint
send_message_wait(GstSoupHTTPSrc * src,const GError * err)2116 send_message_wait (GstSoupHTTPSrc *src, const GError *err)
2117 {
2118   if (err == NULL) {
2119     return 0;
2120   }
2121 
2122   if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_TIMED_OUT) &&
2123       (_soup_message_get_status (src->msg) == SOUP_STATUS_NONE)) {
2124     GST_ERROR_OBJECT (src, "G_IO_ERROR_TIMED_OUT wait begin");
2125     wait_for_connect (src, src->msg, (gint64) src->wait_time - (gint64) src->wait_already);
2126     return -1;
2127   } else if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED)) {
2128     /* if connection closed, still retry */
2129     src->headers_ret = GST_FLOW_CUSTOM_ERROR;
2130     return -1;
2131   }
2132 
2133   return 0;
2134 }
2135 #endif
2136 
2137 struct GstSoupSendSrc
2138 {
2139   GstSoupHTTPSrc *src;
2140   GError *error;
2141 };
2142 
2143 static void
_session_send_cb(GObject * source,GAsyncResult * res,gpointer user_data)2144 _session_send_cb (GObject * source, GAsyncResult * res, gpointer user_data)
2145 {
2146   struct GstSoupSendSrc *msrc = user_data;
2147   GstSoupHTTPSrc *src = msrc->src;
2148   GError *error = NULL;
2149 
2150   g_mutex_lock (&src->session_mutex);
2151 
2152   src->input_stream = _soup_session_send_finish (src->session->session,
2153       res, &error);
2154 
2155 #ifdef OHOS_EXT_FUNC
2156 // ohos.ext.func.0012
2157   if (src->has_sent_first_request == FALSE) {
2158     src->has_sent_first_request = TRUE;
2159   }
2160 #endif
2161 
2162   if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
2163     src->headers_ret = GST_FLOW_FLUSHING;
2164   } else {
2165     src->headers_ret = gst_soup_http_src_got_headers (src, src->msg);
2166   }
2167 
2168   if (!src->input_stream) {
2169     GST_DEBUG_OBJECT (src, "Sending message failed: %s", error->message);
2170 #ifdef OHOS_EXT_FUNC
2171     // ohos.ext.func.0012
2172     if (send_message_wait (src, error) != 0) {
2173       if (error != NULL) {
2174         g_error_free (error);
2175       }
2176       goto done;
2177     }
2178 #endif
2179     msrc->error = error;
2180   }
2181 
2182 #ifdef OHOS_EXT_FUNC
2183 // ohos.ext.func.0012
2184 done:
2185 #endif
2186   g_cond_broadcast (&src->session_cond);
2187   g_mutex_unlock (&src->session_mutex);
2188 }
2189 
2190 static gboolean
_session_send_idle_cb(gpointer user_data)2191 _session_send_idle_cb (gpointer user_data)
2192 {
2193   struct GstSoupSendSrc *msrc = user_data;
2194   GstSoupHTTPSrc *src = msrc->src;
2195 
2196   _soup_session_send_async (src->session->session, src->msg, src->cancellable,
2197       _session_send_cb, msrc);
2198 
2199   return FALSE;
2200 }
2201 
2202 /* called with session lock taken */
2203 static GstFlowReturn
gst_soup_http_src_send_message(GstSoupHTTPSrc * src)2204 gst_soup_http_src_send_message (GstSoupHTTPSrc * src)
2205 {
2206   GstFlowReturn ret;
2207   GSource *source;
2208   struct GstSoupSendSrc msrc;
2209 
2210   g_return_val_if_fail (src->msg != NULL, GST_FLOW_ERROR);
2211   g_assert (src->input_stream == NULL);
2212 
2213   msrc.src = src;
2214   msrc.error = NULL;
2215 
2216   source = g_idle_source_new ();
2217 
2218   src->headers_ret = GST_FLOW_OK;
2219 
2220   g_source_set_callback (source, _session_send_idle_cb, &msrc, NULL);
2221   g_source_attach (source, g_main_loop_get_context (src->session->loop));
2222   g_source_unref (source);
2223 
2224   while (!src->input_stream && !msrc.error)
2225     g_cond_wait (&src->session_cond, &src->session_mutex);
2226 
2227   ret = src->headers_ret;
2228 
2229   if (ret != GST_FLOW_OK) {
2230     goto done;
2231   }
2232 
2233   if (!src->input_stream) {
2234     GST_DEBUG_OBJECT (src, "Didn't get an input stream: %s",
2235         msrc.error->message);
2236     ret = GST_FLOW_ERROR;
2237     goto done;
2238   }
2239 
2240   /* if an input stream exists, it was always successful */
2241   GST_DEBUG_OBJECT (src, "Successfully got a reply");
2242 
2243 done:
2244   g_clear_error (&msrc.error);
2245   return ret;
2246 }
2247 
2248 /* called with session lock taken */
2249 static GstFlowReturn
gst_soup_http_src_do_request(GstSoupHTTPSrc * src,const gchar * method)2250 gst_soup_http_src_do_request (GstSoupHTTPSrc * src, const gchar * method)
2251 {
2252   GstFlowReturn ret;
2253   SoupMessageHeaders *request_headers;
2254 
2255   if (src->max_retries != -1 && src->retry_count > src->max_retries) {
2256     GST_DEBUG_OBJECT (src, "Max retries reached");
2257     return GST_FLOW_ERROR;
2258   }
2259 
2260   src->retry_count++;
2261   /* EOS immediately if we have an empty segment */
2262 #ifdef OHOS_OPT_COMPAT
2263   // ohos.opt.compat.0017
2264   /* Solve the problem of seek probability directly stuck to the end */
2265   if (src->request_position == src->stop_position || src->request_position >= src->content_size)
2266 #else
2267   if (src->request_position == src->stop_position)
2268 #endif
2269     return GST_FLOW_EOS;
2270 
2271   GST_LOG_OBJECT (src, "Running request for method: %s", method);
2272 
2273   if (src->msg)
2274     request_headers = _soup_message_get_request_headers (src->msg);
2275 
2276   /* Update the position if we are retrying */
2277   if (src->msg && src->request_position > 0) {
2278     gst_soup_http_src_add_range_header (src, src->request_position,
2279         src->stop_position);
2280   } else if (src->msg && src->request_position == 0)
2281     _soup_message_headers_remove (request_headers, "Range");
2282 
2283   /* add_range_header() has the side effect of setting read_position to
2284    * the requested position. This *needs* to be set regardless of having
2285    * a message or not. Failure to do so would result in calculation being
2286    * done with stale/wrong read position */
2287   src->read_position = src->request_position;
2288 
2289   if (!src->msg) {
2290     if (!gst_soup_http_src_build_message (src, method)) {
2291       return GST_FLOW_ERROR;
2292     }
2293   }
2294 
2295   if (g_cancellable_is_cancelled (src->cancellable)) {
2296     GST_INFO_OBJECT (src, "interrupted");
2297     return GST_FLOW_FLUSHING;
2298   }
2299 
2300   ret = gst_soup_http_src_send_message (src);
2301 
2302   /* Check if Range header was respected. */
2303   if (ret == GST_FLOW_OK && src->request_position > 0 &&
2304       _soup_message_get_status (src->msg) != SOUP_STATUS_PARTIAL_CONTENT) {
2305     src->seekable = FALSE;
2306     GST_ELEMENT_ERROR_WITH_DETAILS (src, RESOURCE, SEEK,
2307         (_("Server does not support seeking.")),
2308         ("Server does not accept Range HTTP header, URL: %s, Redirect to: %s",
2309             src->location, GST_STR_NULL (src->redirection_uri)),
2310         ("http-status-code", G_TYPE_UINT, _soup_message_get_status (src->msg),
2311             "http-redirection-uri", G_TYPE_STRING,
2312             GST_STR_NULL (src->redirection_uri), NULL));
2313     ret = GST_FLOW_ERROR;
2314   }
2315 
2316   return ret;
2317 }
2318 
2319 /*
2320  * Check if the bytes_read is above a certain threshold of the blocksize, if
2321  * that happens a few times in a row, increase the blocksize; Do the same in
2322  * the opposite direction to reduce the blocksize.
2323  */
2324 static void
gst_soup_http_src_check_update_blocksize(GstSoupHTTPSrc * src,gint64 bytes_read)2325 gst_soup_http_src_check_update_blocksize (GstSoupHTTPSrc * src,
2326     gint64 bytes_read)
2327 {
2328   guint blocksize = gst_base_src_get_blocksize (GST_BASE_SRC_CAST (src));
2329 
2330   gint64 time_since_last_read =
2331       g_get_monotonic_time () * GST_USECOND - src->last_socket_read_time;
2332 
2333   GST_LOG_OBJECT (src, "Checking to update blocksize. Read: %" G_GINT64_FORMAT
2334       " bytes, blocksize: %u bytes, time since last read: %" GST_TIME_FORMAT,
2335       bytes_read, blocksize, GST_TIME_ARGS (time_since_last_read));
2336 
2337   if (bytes_read >= blocksize * GROW_BLOCKSIZE_LIMIT
2338       && time_since_last_read <= GROW_TIME_LIMIT) {
2339     src->reduce_blocksize_count = 0;
2340     src->increase_blocksize_count++;
2341 
2342     if (src->increase_blocksize_count >= GROW_BLOCKSIZE_COUNT) {
2343       blocksize *= GROW_BLOCKSIZE_FACTOR;
2344       GST_DEBUG_OBJECT (src, "Increased blocksize to %u", blocksize);
2345       gst_base_src_set_blocksize (GST_BASE_SRC_CAST (src), blocksize);
2346       src->increase_blocksize_count = 0;
2347     }
2348   } else if (bytes_read < blocksize * REDUCE_BLOCKSIZE_LIMIT
2349       || time_since_last_read > GROW_TIME_LIMIT) {
2350     src->reduce_blocksize_count++;
2351     src->increase_blocksize_count = 0;
2352 
2353     if (src->reduce_blocksize_count >= REDUCE_BLOCKSIZE_COUNT) {
2354       blocksize *= REDUCE_BLOCKSIZE_FACTOR;
2355       blocksize = MAX (blocksize, src->minimum_blocksize);
2356       GST_DEBUG_OBJECT (src, "Decreased blocksize to %u", blocksize);
2357       gst_base_src_set_blocksize (GST_BASE_SRC_CAST (src), blocksize);
2358       src->reduce_blocksize_count = 0;
2359     }
2360   } else {
2361     src->reduce_blocksize_count = src->increase_blocksize_count = 0;
2362   }
2363 }
2364 
2365 static void
gst_soup_http_src_update_position(GstSoupHTTPSrc * src,gint64 bytes_read)2366 gst_soup_http_src_update_position (GstSoupHTTPSrc * src, gint64 bytes_read)
2367 {
2368   GstBaseSrc *basesrc = GST_BASE_SRC_CAST (src);
2369   guint64 new_position;
2370 
2371   new_position = src->read_position + bytes_read;
2372   if (G_LIKELY (src->request_position == src->read_position))
2373     src->request_position = new_position;
2374   src->read_position = new_position;
2375 
2376   if (src->have_size) {
2377     if (new_position > src->content_size) {
2378       GST_DEBUG_OBJECT (src, "Got position previous estimated content size "
2379           "(%" G_GINT64_FORMAT " > %" G_GINT64_FORMAT ")", new_position,
2380           src->content_size);
2381       src->content_size = new_position;
2382       basesrc->segment.duration = src->content_size;
2383       gst_element_post_message (GST_ELEMENT (src),
2384           gst_message_new_duration_changed (GST_OBJECT (src)));
2385     } else if (new_position == src->content_size) {
2386       GST_DEBUG_OBJECT (src, "We're EOS now");
2387     }
2388   }
2389 }
2390 
2391 struct GstSoupReadResult
2392 {
2393   GstSoupHTTPSrc *src;
2394   GError *error;
2395   void *buffer;
2396   gsize bufsize;
2397   gssize nbytes;
2398 };
2399 
2400 static void
_session_read_cb(GObject * source,GAsyncResult * ret,gpointer user_data)2401 _session_read_cb (GObject * source, GAsyncResult * ret, gpointer user_data)
2402 {
2403   struct GstSoupReadResult *res = user_data;
2404 
2405   g_mutex_lock (&res->src->session_mutex);
2406 
2407   res->nbytes = g_input_stream_read_finish (G_INPUT_STREAM (source),
2408       ret, &res->error);
2409 
2410   g_cond_signal (&res->src->session_cond);
2411   g_mutex_unlock (&res->src->session_mutex);
2412 }
2413 
2414 static gboolean
_session_read_idle_cb(gpointer user_data)2415 _session_read_idle_cb (gpointer user_data)
2416 {
2417   struct GstSoupReadResult *res = user_data;
2418 
2419   g_input_stream_read_async (res->src->input_stream, res->buffer,
2420       res->bufsize, G_PRIORITY_DEFAULT, res->src->cancellable,
2421       _session_read_cb, res);
2422 
2423   return FALSE;
2424 }
2425 
2426 static GstFlowReturn
gst_soup_http_src_read_buffer(GstSoupHTTPSrc * src,GstBuffer ** outbuf)2427 gst_soup_http_src_read_buffer (GstSoupHTTPSrc * src, GstBuffer ** outbuf)
2428 {
2429   struct GstSoupReadResult res;
2430   GstMapInfo mapinfo;
2431   GstBaseSrc *bsrc;
2432   GstFlowReturn ret;
2433   GSource *source;
2434 
2435   bsrc = GST_BASE_SRC_CAST (src);
2436 
2437   *outbuf = gst_soup_http_src_alloc_buffer (src);
2438   if (!*outbuf) {
2439     GST_WARNING_OBJECT (src, "Failed to allocate buffer");
2440     return GST_FLOW_ERROR;
2441   }
2442 
2443   if (!gst_buffer_map (*outbuf, &mapinfo, GST_MAP_WRITE)) {
2444     GST_WARNING_OBJECT (src, "Failed to map buffer");
2445     return GST_FLOW_ERROR;
2446   }
2447 
2448   res.src = src;
2449   res.buffer = mapinfo.data;
2450   res.bufsize = mapinfo.size;
2451   res.error = NULL;
2452   res.nbytes = -1;
2453 
2454   source = g_idle_source_new ();
2455 
2456   g_mutex_lock (&src->session_mutex);
2457 
2458   g_source_set_callback (source, _session_read_idle_cb, &res, NULL);
2459   /* invoke on libsoup thread */
2460   g_source_attach (source, g_main_loop_get_context (src->session->loop));
2461   g_source_unref (source);
2462 
2463   /* wait for it */
2464   while (!res.error && res.nbytes < 0)
2465     g_cond_wait (&src->session_cond, &src->session_mutex);
2466   g_mutex_unlock (&src->session_mutex);
2467 
2468   GST_DEBUG_OBJECT (src, "Read %" G_GSSIZE_FORMAT " bytes from http input",
2469       res.nbytes);
2470 
2471   if (res.error) {
2472     /* retry by default */
2473     GstFlowReturn ret = GST_FLOW_CUSTOM_ERROR;
2474     if (g_error_matches (res.error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
2475       ret = GST_FLOW_FLUSHING;
2476     } else {
2477       GST_ERROR_OBJECT (src, "Got error from libsoup: %s", res.error->message);
2478     }
2479     g_error_free (res.error);
2480     gst_buffer_unmap (*outbuf, &mapinfo);
2481     gst_buffer_unref (*outbuf);
2482     return ret;
2483   }
2484 
2485   gst_buffer_unmap (*outbuf, &mapinfo);
2486   if (res.nbytes > 0) {
2487     gst_buffer_set_size (*outbuf, res.nbytes);
2488     GST_BUFFER_OFFSET (*outbuf) = bsrc->segment.position;
2489     ret = GST_FLOW_OK;
2490     gst_soup_http_src_update_position (src, res.nbytes);
2491 
2492     /* Got some data, reset retry counter */
2493     src->retry_count = 0;
2494 
2495     gst_soup_http_src_check_update_blocksize (src, res.nbytes);
2496 
2497     src->last_socket_read_time = g_get_monotonic_time () * GST_USECOND;
2498 
2499     /* If we're at the end of a range request, read again to let libsoup
2500      * finalize the request. This allows to reuse the connection again later,
2501      * otherwise we would have to cancel the message and close the connection
2502      */
2503     if (bsrc->segment.stop != -1
2504         && bsrc->segment.position + res.nbytes >= bsrc->segment.stop) {
2505       SoupMessage *msg = src->msg;
2506       guint8 tmp[128];
2507 
2508       res.buffer = tmp;
2509       res.bufsize = sizeof (tmp);
2510       res.nbytes = -1;
2511 
2512       src->msg = NULL;
2513       src->have_body = TRUE;
2514 
2515       g_mutex_lock (&src->session_mutex);
2516 
2517       source = g_idle_source_new ();
2518 
2519       g_source_set_callback (source, _session_read_idle_cb, &res, NULL);
2520       /* This should return immediately as we're at the end of the range */
2521       g_source_attach (source, g_main_loop_get_context (src->session->loop));
2522       g_source_unref (source);
2523 
2524       while (!res.error && res.nbytes < 0)
2525         g_cond_wait (&src->session_cond, &src->session_mutex);
2526       g_mutex_unlock (&src->session_mutex);
2527 
2528       g_clear_error (&res.error);
2529       g_object_unref (msg);
2530 
2531       if (res.nbytes > 0)
2532         GST_ERROR_OBJECT (src,
2533             "Read %" G_GSIZE_FORMAT " bytes after end of range", res.nbytes);
2534     }
2535   } else {
2536     gst_buffer_unref (*outbuf);
2537     if (src->have_size && src->read_position < src->content_size) {
2538       /* Maybe the server disconnected, retry */
2539       ret = GST_FLOW_CUSTOM_ERROR;
2540     } else {
2541       g_clear_object (&src->msg);
2542       src->msg = NULL;
2543       ret = GST_FLOW_EOS;
2544       src->have_body = TRUE;
2545     }
2546   }
2547 
2548   g_clear_error (&res.error);
2549 
2550   return ret;
2551 }
2552 
2553 static gboolean
_session_stream_clear_cb(gpointer user_data)2554 _session_stream_clear_cb (gpointer user_data)
2555 {
2556   GstSoupHTTPSrc *src = user_data;
2557 
2558   g_mutex_lock (&src->session_mutex);
2559 
2560   g_clear_object (&src->input_stream);
2561 
2562 #ifndef OHOS_EXT_FUNC
2563   // ohos.ext.func.0012
2564   g_cond_broadcast (&src->session_cond);
2565   src->retry_count = 0;
2566 #else
2567   g_cond_signal (&src->session_cond);
2568 #endif
2569 
2570   g_mutex_unlock (&src->session_mutex);
2571 
2572   return FALSE;
2573 }
2574 
2575 static void
gst_soup_http_src_stream_clear(GstSoupHTTPSrc * src)2576 gst_soup_http_src_stream_clear (GstSoupHTTPSrc * src)
2577 {
2578   GSource *source;
2579 
2580   if (!src->input_stream)
2581     return;
2582 
2583   g_mutex_lock (&src->session_mutex);
2584 
2585   source = g_idle_source_new ();
2586 
2587   g_source_set_callback (source, _session_stream_clear_cb, src, NULL);
2588   g_source_attach (source, g_main_loop_get_context (src->session->loop));
2589   g_source_unref (source);
2590 
2591   while (src->input_stream)
2592     g_cond_wait (&src->session_cond, &src->session_mutex);
2593 
2594   g_mutex_unlock (&src->session_mutex);
2595 }
2596 
2597 static GstFlowReturn
gst_soup_http_src_create(GstPushSrc * psrc,GstBuffer ** outbuf)2598 gst_soup_http_src_create (GstPushSrc * psrc, GstBuffer ** outbuf)
2599 {
2600   GstSoupHTTPSrc *src;
2601   GstFlowReturn ret = GST_FLOW_OK;
2602   GstEvent *http_headers_event = NULL;
2603 
2604   src = GST_SOUP_HTTP_SRC (psrc);
2605 
2606 retry:
2607 
2608   /* Check for pending position change */
2609   if (src->request_position != src->read_position && src->input_stream) {
2610     gst_soup_http_src_stream_clear (src);
2611   }
2612 
2613   if (g_cancellable_is_cancelled (src->cancellable)) {
2614     ret = GST_FLOW_FLUSHING;
2615     goto done;
2616   }
2617 
2618   /* If we have no open connection to the server, start one */
2619   if (!src->input_stream) {
2620     *outbuf = NULL;
2621     g_mutex_lock (&src->session_mutex);
2622     ret =
2623         gst_soup_http_src_do_request (src,
2624         src->method ? src->method : SOUP_METHOD_GET);
2625     http_headers_event = src->http_headers_event;
2626     src->http_headers_event = NULL;
2627     g_mutex_unlock (&src->session_mutex);
2628   }
2629 
2630   if (ret == GST_FLOW_OK || ret == GST_FLOW_CUSTOM_ERROR) {
2631     if (http_headers_event) {
2632       gst_pad_push_event (GST_BASE_SRC_PAD (src), http_headers_event);
2633       http_headers_event = NULL;
2634     }
2635   }
2636 
2637   if (ret == GST_FLOW_OK)
2638     ret = gst_soup_http_src_read_buffer (src, outbuf);
2639 
2640 done:
2641   GST_DEBUG_OBJECT (src, "Returning %d %s", ret, gst_flow_get_name (ret));
2642   if (ret != GST_FLOW_OK) {
2643     if (http_headers_event)
2644       gst_event_unref (http_headers_event);
2645 
2646     if (src->input_stream) {
2647       gst_soup_http_src_stream_clear (src);
2648     }
2649     if (ret == GST_FLOW_CUSTOM_ERROR) {
2650       ret = GST_FLOW_OK;
2651       goto retry;
2652     }
2653   }
2654 
2655   if (ret == GST_FLOW_FLUSHING) {
2656     src->retry_count = 0;
2657   }
2658 
2659   return ret;
2660 }
2661 
2662 static gboolean
gst_soup_http_src_start(GstBaseSrc * bsrc)2663 gst_soup_http_src_start (GstBaseSrc * bsrc)
2664 {
2665   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (bsrc);
2666   gboolean ret;
2667 
2668   GST_DEBUG_OBJECT (src, "start(\"%s\")", src->location);
2669 
2670   g_mutex_lock (&src->session_mutex);
2671   ret = gst_soup_http_src_session_open (src);
2672   g_mutex_unlock (&src->session_mutex);
2673   return ret;
2674 }
2675 
2676 static gboolean
gst_soup_http_src_stop(GstBaseSrc * bsrc)2677 gst_soup_http_src_stop (GstBaseSrc * bsrc)
2678 {
2679   GstSoupHTTPSrc *src;
2680 
2681   src = GST_SOUP_HTTP_SRC (bsrc);
2682   GST_DEBUG_OBJECT (src, "stop()");
2683 
2684   gst_soup_http_src_stream_clear (src);
2685 
2686   if (src->keep_alive && !src->msg && !src->session_is_shared)
2687     g_cancellable_cancel (src->cancellable);
2688   else
2689     gst_soup_http_src_session_close (src);
2690 
2691   gst_soup_http_src_reset (src);
2692   return TRUE;
2693 }
2694 
2695 static GstStateChangeReturn
gst_soup_http_src_change_state(GstElement * element,GstStateChange transition)2696 gst_soup_http_src_change_state (GstElement * element, GstStateChange transition)
2697 {
2698   GstStateChangeReturn ret;
2699   GstSoupHTTPSrc *src;
2700 
2701   src = GST_SOUP_HTTP_SRC (element);
2702 
2703   switch (transition) {
2704     case GST_STATE_CHANGE_READY_TO_NULL:
2705       gst_soup_http_src_session_close (src);
2706       break;
2707 #ifdef OHOS_EXT_FUNC
2708     // ohos.ext.func.0012
2709     case GST_STATE_CHANGE_PAUSED_TO_READY:{
2710       GST_DEBUG_OBJECT (src, "souphttpsrc paused_to_ready");
2711       src->has_sent_first_request = FALSE;
2712       src->has_received_first_response = FALSE;
2713       break;
2714     }
2715     case GST_STATE_CHANGE_READY_TO_PAUSED:{
2716       GST_DEBUG_OBJECT (src, "souphttpsrc ready_to_paused");
2717       src->has_sent_first_request = FALSE;
2718       src->has_received_first_response = FALSE;
2719       g_mutex_lock (&src->wait_lock);
2720       src->exit_block = FALSE;
2721       g_mutex_unlock (&src->wait_lock);
2722       break;
2723     }
2724 #endif
2725     default:
2726       break;
2727   }
2728 
2729   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2730 
2731   return ret;
2732 }
2733 
2734 static void
gst_soup_http_src_set_context(GstElement * element,GstContext * context)2735 gst_soup_http_src_set_context (GstElement * element, GstContext * context)
2736 {
2737   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (element);
2738 
2739   if (g_strcmp0 (gst_context_get_context_type (context),
2740           GST_SOUP_SESSION_CONTEXT) == 0) {
2741     const GstStructure *s = gst_context_get_structure (context);
2742 
2743     GST_OBJECT_LOCK (src);
2744 
2745     g_clear_object (&src->external_session);
2746     gst_structure_get (s, "session", GST_TYPE_SOUP_SESSION,
2747         &src->external_session, NULL);
2748 
2749     GST_DEBUG_OBJECT (src, "Setting external session %p",
2750         src->external_session);
2751     GST_OBJECT_UNLOCK (src);
2752   }
2753 
2754   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
2755 }
2756 
2757 /* Interrupt a blocking request. */
2758 static gboolean
gst_soup_http_src_unlock(GstBaseSrc * bsrc)2759 gst_soup_http_src_unlock (GstBaseSrc * bsrc)
2760 {
2761   GstSoupHTTPSrc *src;
2762 
2763   src = GST_SOUP_HTTP_SRC (bsrc);
2764   GST_DEBUG_OBJECT (src, "unlock()");
2765 
2766   g_cancellable_cancel (src->cancellable);
2767   return TRUE;
2768 }
2769 
2770 /* Interrupt interrupt. */
2771 static gboolean
gst_soup_http_src_unlock_stop(GstBaseSrc * bsrc)2772 gst_soup_http_src_unlock_stop (GstBaseSrc * bsrc)
2773 {
2774   GstSoupHTTPSrc *src;
2775 
2776   src = GST_SOUP_HTTP_SRC (bsrc);
2777   GST_DEBUG_OBJECT (src, "unlock_stop()");
2778 
2779   g_cancellable_reset (src->cancellable);
2780   return TRUE;
2781 }
2782 
2783 static gboolean
gst_soup_http_src_get_size(GstBaseSrc * bsrc,guint64 * size)2784 gst_soup_http_src_get_size (GstBaseSrc * bsrc, guint64 * size)
2785 {
2786   GstSoupHTTPSrc *src;
2787 
2788   src = GST_SOUP_HTTP_SRC (bsrc);
2789 
2790   if (src->have_size) {
2791     GST_DEBUG_OBJECT (src, "get_size() = %" G_GUINT64_FORMAT,
2792         src->content_size);
2793     *size = src->content_size;
2794     return TRUE;
2795   }
2796   GST_DEBUG_OBJECT (src, "get_size() = FALSE");
2797   return FALSE;
2798 }
2799 
2800 static void
gst_soup_http_src_check_seekable(GstSoupHTTPSrc * src)2801 gst_soup_http_src_check_seekable (GstSoupHTTPSrc * src)
2802 {
2803   GstFlowReturn ret = GST_FLOW_OK;
2804 
2805   /* Special case to check if the server allows range requests
2806    * before really starting to get data in the buffer creation
2807    * loops.
2808    */
2809   if (!src->got_headers && GST_STATE (src) >= GST_STATE_PAUSED) {
2810     g_mutex_lock (&src->session_mutex);
2811     while (!src->got_headers && !g_cancellable_is_cancelled (src->cancellable)
2812         && ret == GST_FLOW_OK) {
2813       if ((src->msg && _soup_message_get_method (src->msg) != SOUP_METHOD_HEAD)) {
2814         /* wait for the current request to finish */
2815         g_cond_wait (&src->session_cond, &src->session_mutex);
2816         ret = src->headers_ret;
2817       } else {
2818         if (gst_soup_http_src_session_open (src)) {
2819           ret = gst_soup_http_src_do_request (src, SOUP_METHOD_HEAD);
2820         }
2821       }
2822     }
2823     g_mutex_unlock (&src->session_mutex);
2824   }
2825 }
2826 
2827 static gboolean
gst_soup_http_src_is_seekable(GstBaseSrc * bsrc)2828 gst_soup_http_src_is_seekable (GstBaseSrc * bsrc)
2829 {
2830   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (bsrc);
2831 
2832   gst_soup_http_src_check_seekable (src);
2833 
2834   return src->seekable;
2835 }
2836 
2837 static gboolean
gst_soup_http_src_do_seek(GstBaseSrc * bsrc,GstSegment * segment)2838 gst_soup_http_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment)
2839 {
2840   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (bsrc);
2841 
2842   GST_DEBUG_OBJECT (src, "do_seek(%" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT
2843       ")", segment->start, segment->stop);
2844   if (src->read_position == segment->start &&
2845       src->request_position == src->read_position &&
2846       src->stop_position == segment->stop) {
2847     GST_DEBUG_OBJECT (src,
2848         "Seek to current read/end position and no seek pending");
2849     return TRUE;
2850   }
2851 
2852   gst_soup_http_src_check_seekable (src);
2853 
2854   /* If we have no headers we don't know yet if it is seekable or not.
2855    * Store the start position and error out later if it isn't */
2856   if (src->got_headers && !src->seekable) {
2857     GST_WARNING_OBJECT (src, "Not seekable");
2858     return FALSE;
2859   }
2860 
2861   if (segment->rate < 0.0 || segment->format != GST_FORMAT_BYTES) {
2862     GST_WARNING_OBJECT (src, "Invalid seek segment");
2863     return FALSE;
2864   }
2865 
2866   if (src->have_size && segment->start >= src->content_size) {
2867     GST_WARNING_OBJECT (src,
2868         "Potentially seeking behind end of file, might EOS immediately");
2869   }
2870 
2871   /* Wait for create() to handle the jump in offset. */
2872   src->request_position = segment->start;
2873   src->stop_position = segment->stop;
2874 
2875   return TRUE;
2876 }
2877 
2878 static gboolean
gst_soup_http_src_query(GstBaseSrc * bsrc,GstQuery * query)2879 gst_soup_http_src_query (GstBaseSrc * bsrc, GstQuery * query)
2880 {
2881   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (bsrc);
2882   gboolean ret;
2883   GstSchedulingFlags flags;
2884   gint minsize, maxsize, align;
2885 
2886   switch (GST_QUERY_TYPE (query)) {
2887     case GST_QUERY_URI:
2888       gst_query_set_uri (query, src->location);
2889       if (src->redirection_uri != NULL) {
2890         gst_query_set_uri_redirection (query, src->redirection_uri);
2891         gst_query_set_uri_redirection_permanent (query,
2892             src->redirection_permanent);
2893       }
2894       ret = TRUE;
2895       break;
2896     default:
2897       ret = FALSE;
2898       break;
2899   }
2900 
2901   if (!ret)
2902     ret = GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query);
2903 
2904   switch (GST_QUERY_TYPE (query)) {
2905     case GST_QUERY_SCHEDULING:
2906       gst_query_parse_scheduling (query, &flags, &minsize, &maxsize, &align);
2907       flags |= GST_SCHEDULING_FLAG_BANDWIDTH_LIMITED;
2908       gst_query_set_scheduling (query, flags, minsize, maxsize, align);
2909       break;
2910     default:
2911       break;
2912   }
2913 
2914   return ret;
2915 }
2916 
2917 static gboolean
gst_soup_http_src_set_location(GstSoupHTTPSrc * src,const gchar * uri,GError ** error)2918 gst_soup_http_src_set_location (GstSoupHTTPSrc * src, const gchar * uri,
2919     GError ** error)
2920 {
2921   const char *alt_schemes[] = { "icy://", "icyx://" };
2922   guint i;
2923 
2924   if (src->location) {
2925     g_free (src->location);
2926     src->location = NULL;
2927   }
2928 
2929   if (uri == NULL)
2930     return FALSE;
2931 
2932   for (i = 0; i < G_N_ELEMENTS (alt_schemes); i++) {
2933     if (g_str_has_prefix (uri, alt_schemes[i])) {
2934       src->location =
2935           g_strdup_printf ("http://%s", uri + strlen (alt_schemes[i]));
2936       return TRUE;
2937     }
2938   }
2939 
2940   if (src->redirection_uri) {
2941     g_free (src->redirection_uri);
2942     src->redirection_uri = NULL;
2943   }
2944 
2945   src->location = g_strdup (uri);
2946 
2947   return TRUE;
2948 }
2949 
2950 static gboolean
gst_soup_http_src_set_proxy(GstSoupHTTPSrc * src,const gchar * uri)2951 gst_soup_http_src_set_proxy (GstSoupHTTPSrc * src, const gchar * uri)
2952 {
2953   if (src->proxy) {
2954     gst_soup_uri_free (src->proxy);
2955     src->proxy = NULL;
2956   }
2957 
2958   if (uri == NULL || *uri == '\0')
2959     return TRUE;
2960 
2961   if (g_strstr_len (uri, -1, "://")) {
2962     src->proxy = gst_soup_uri_new (uri);
2963   } else {
2964     gchar *new_uri = g_strconcat ("http://", uri, NULL);
2965 
2966     src->proxy = gst_soup_uri_new (new_uri);
2967     g_free (new_uri);
2968   }
2969 
2970   return (src->proxy != NULL);
2971 }
2972 
2973 static GstURIType
gst_soup_http_src_uri_get_type(GType type)2974 gst_soup_http_src_uri_get_type (GType type)
2975 {
2976   return GST_URI_SRC;
2977 }
2978 
2979 static const gchar *const *
gst_soup_http_src_uri_get_protocols(GType type)2980 gst_soup_http_src_uri_get_protocols (GType type)
2981 {
2982 #ifdef OHOS_EXT_FUNC
2983   /* ohos.ext.func.0025 disable https, https will be supported with curlhttpsrc */
2984   static const gchar *protocols[] = { "http", "icy", "icyx", NULL };
2985 #else
2986   static const gchar *protocols[] = { "http", "https", "icy", "icyx", NULL };
2987 #endif
2988 
2989   return protocols;
2990 }
2991 
2992 static gchar *
gst_soup_http_src_uri_get_uri(GstURIHandler * handler)2993 gst_soup_http_src_uri_get_uri (GstURIHandler * handler)
2994 {
2995   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (handler);
2996 
2997   /* FIXME: make thread-safe */
2998   return g_strdup (src->location);
2999 }
3000 
3001 static gboolean
gst_soup_http_src_uri_set_uri(GstURIHandler * handler,const gchar * uri,GError ** error)3002 gst_soup_http_src_uri_set_uri (GstURIHandler * handler, const gchar * uri,
3003     GError ** error)
3004 {
3005   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (handler);
3006 
3007   return gst_soup_http_src_set_location (src, uri, error);
3008 }
3009 
3010 static void
gst_soup_http_src_uri_handler_init(gpointer g_iface,gpointer iface_data)3011 gst_soup_http_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
3012 {
3013   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
3014 
3015   iface->get_type = gst_soup_http_src_uri_get_type;
3016   iface->get_protocols = gst_soup_http_src_uri_get_protocols;
3017   iface->get_uri = gst_soup_http_src_uri_get_uri;
3018   iface->set_uri = gst_soup_http_src_uri_set_uri;
3019 }
3020 
3021 static gboolean
souphttpsrc_element_init(GstPlugin * plugin)3022 souphttpsrc_element_init (GstPlugin * plugin)
3023 {
3024   gboolean ret = TRUE;
3025 
3026   GST_DEBUG_CATEGORY_INIT (souphttpsrc_debug, "souphttpsrc", 0,
3027       "SOUP HTTP src");
3028 
3029   if (!soup_element_init (plugin))
3030     return TRUE;
3031 
3032   ret = gst_element_register (plugin, "souphttpsrc",
3033       GST_RANK_PRIMARY, GST_TYPE_SOUP_HTTP_SRC);
3034 
3035   return ret;
3036 }
3037