21 #include "mozilla-config.h"
24 #include <gst/base/gstpushsrc.h>
25 #include <gst/base/gstadapter.h>
27 #include <nsStringAPI.h>
28 #include <nsNetUtil.h>
29 #include <nsComponentManagerUtils.h>
30 #include <nsIProtocolHandler.h>
31 #include <nsIComponentRegistrar.h>
33 #include <nsISupportsPrimitives.h>
34 #include <nsIResumableChannel.h>
35 #include <nsIRunnable.h>
36 #include <nsIHttpHeaderVisitor.h>
37 #include <nsIHttpEventSink.h>
38 #include <nsThreadUtils.h>
39 #include <nsIWindowWatcher.h>
40 #include <nsIAuthPrompt.h>
92 #define GST_CAT_DEFAULT mozillasrc_debug
95 GST_ELEMENT_DETAILS ((gchar *)
"Mozilla nsIInputStream source",
96 (gchar *)
"Source/Network",
97 (gchar *)
"Receive data from a hosting mozilla "
98 "application Mozilla's I/O APIs",
99 (gchar *)
"Pioneers of the Inevitable <songbird@songbirdnest.com");
101 static GstStaticPadTemplate
srctemplate = GST_STATIC_PAD_TEMPLATE (
"src",
104 GST_STATIC_CAPS_ANY);
114 gpointer iface_data);
118 const GValue *
value, GParamSpec * pspec);
120 GValue *
value, GParamSpec * pspec);
123 GstBuffer ** outbuf);
130 GstSegment * segment);
147 g_object_unref (mSrc);
159 if (mSrc->suspended) {
160 nsCOMPtr<nsIRequest> request(do_QueryInterface(mSrc->channel));
162 GST_DEBUG_OBJECT (mSrc,
"Resuming request...");
164 mSrc->suspended = FALSE;
172 public nsIHttpHeaderVisitor,
174 public nsIHttpEventSink
178 NS_DECL_NSIREQUESTOBSERVER
179 NS_DECL_NSISTREAMLISTENER
180 NS_DECL_NSIHTTPHEADERVISITOR
181 NS_DECL_NSIINTERFACEREQUESTOR
182 NS_DECL_NSIHTTPEVENTSINK
189 GstAdapter *mAdapter;
191 void ReadHeaders (gchar *
data);
192 GstBuffer *ScanForHeaders(GstBuffer *buf);
204 g_object_unref (mAdapter);
210 nsIHttpHeaderVisitor,
217 if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
218 nsCOMPtr<nsIWindowWatcher> wwatch(
219 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
220 return wwatch->GetNewAuthPrompter(NULL, (nsIAuthPrompt**)aResult);
222 else if (aIID.Equals(NS_GET_IID(nsIHttpEventSink))) {
226 return NS_ERROR_NO_INTERFACE;
230 StreamListener::OnRedirect(nsIHttpChannel *httpChannel,
nsIChannel *newChannel)
232 nsCOMPtr<nsIChannel> channel(do_QueryInterface(newChannel));
234 GST_DEBUG_OBJECT (mSrc,
"Redirecting, got new channel");
243 StreamListener::VisitHeader(
const nsACString &
header,
const nsACString &
value)
252 StreamListener::OnStartRequest(nsIRequest *req,
nsISupports *ctxt)
254 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(req));
258 nsCAutoString acceptRangesHeader;
259 nsCAutoString icyHeader;
262 if (NS_SUCCEEDED(httpChannel->GetRequestSucceeded(&succeeded))
266 PRUint32 responsecode;
267 nsCString responsetext;
269 rv = httpChannel->GetResponseStatus(&responsecode);
270 NS_ENSURE_SUCCESS(rv, rv);
271 rv = httpChannel->GetResponseStatusText(responsetext);
272 NS_ENSURE_SUCCESS(rv, rv);
274 GST_INFO_OBJECT (mSrc,
"HTTP Response %d (%s)", responsecode,
280 nsCOMPtr<nsIChannel> channel(do_QueryInterface(req));
281 if (mSrc->
channel == channel) {
282 GST_ELEMENT_ERROR (mSrc, RESOURCE, READ,
283 (
"Could not read from URL %s", mSrc->
location),
284 (
"HTTP response code %d (%s) when fetching uri %s",
285 responsecode, responsetext.get(),
299 rv = httpChannel->VisitResponseHeaders(
this);
303 rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING(
"Accept-Ranges"),
305 if (NS_FAILED (rv) || acceptRangesHeader.IsEmpty()) {
306 GST_DEBUG_OBJECT (mSrc,
"No Accept-Ranges header in response; "
307 "setting is_seekable to false");
310 if (acceptRangesHeader.Find (NS_LITERAL_CSTRING (
"bytes")) < 0) {
311 GST_DEBUG_OBJECT (mSrc,
"No 'bytes' in Accept-Ranges header; "
312 "setting is_seekable to false");
316 GST_DEBUG_OBJECT (mSrc,
"Accept-Ranges header includes 'bytes' field, "
317 "seeking supported");
323 ((GstBaseSrc *)mSrc)->seekable = FALSE;
328 rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING(
"icy-metaint"),
330 if (NS_SUCCEEDED (rv) && !icyHeader.IsEmpty()) {
331 int metadata_interval;
332 GST_DEBUG_OBJECT (mSrc,
"Received icy-metaint header, parsing it");
334 metadata_interval = icyHeader.ToInteger(&rv);
335 if (NS_FAILED (rv)) {
336 GST_WARNING_OBJECT (mSrc,
"Could not parse icy-metaint header");
339 GST_DEBUG_OBJECT (mSrc,
"Using icy caps with metadata interval %d",
344 mSrc->
icy_caps = gst_caps_new_simple (
"application/x-icy",
345 "metadata-interval", G_TYPE_INT, metadata_interval, NULL);
349 GST_DEBUG_OBJECT (mSrc,
"No icy-metaint header found");
355 nsCOMPtr<nsIPropertyBag2> properties(do_QueryInterface(mSrc->
channel));
359 rv = properties->GetPropertyAsInt64 (NS_LITERAL_STRING (
"content-length"),
362 if (NS_SUCCEEDED(rv) && length != -1) {
364 GST_DEBUG_OBJECT (mSrc,
"Read content length: %" G_GINT64_FORMAT,
367 gst_segment_set_duration (&((GstBaseSrc *)mSrc)->segment,
369 gst_element_post_message (GST_ELEMENT (mSrc),
370 gst_message_new_duration (GST_OBJECT (mSrc), GST_FORMAT_BYTES,
374 GST_DEBUG_OBJECT (mSrc,
"No content-length found");
378 GST_DEBUG_OBJECT (mSrc,
"%p::StreamListener::OnStartRequest called; "
379 "connection made",
this);
384 StreamListener::OnStopRequest(nsIRequest *req,
nsISupports *ctxt,
387 GST_DEBUG_OBJECT (mSrc,
"%p::StreamListener::OnStopRequest called; "
388 "connection lost",
this);
399 GST_ELEMENT_ERROR (mSrc, RESOURCE, READ,
400 (
"Could not read from URL %s", mSrc->
location),
401 (
"nsresult %d", status));
406 GST_DEBUG_OBJECT (mSrc,
"At EOS after request stopped");
415 void StreamListener::ReadHeaders (gchar *
data)
417 gchar **lines = g_strsplit (data,
"\r\n", 0);
422 gchar **vals = g_strsplit_set (*line,
": ", 2);
423 if (vals[0] && vals[1]) {
426 gchar *
value = vals[1];
428 GST_DEBUG (
"Read header: '%s' : '%s'", header, value);
431 if (g_ascii_strcasecmp (header,
"icy-metaint") == 0) {
432 int metaint = g_ascii_strtoll (value, NULL, 10);
435 GST_DEBUG (
"icy-metaint read: %d", metaint);
438 mSrc->
icy_caps = gst_caps_new_simple (
"application/x-icy",
439 "metadata-interval", G_TYPE_INT, metaint, NULL);
451 GstBuffer *StreamListener::ScanForHeaders(GstBuffer *buf)
458 mAdapter = gst_adapter_new();
459 gst_adapter_push (mAdapter, buf);
461 len = gst_adapter_available (mAdapter);
462 data = (gchar *)gst_adapter_peek (mAdapter, len);
464 end = g_strrstr_len (data, len,
"\r\n\r\n");
474 GST_DEBUG (
"Reading headers from '%s'", data);
475 ReadHeaders ((gchar *)data);
477 len = end - data + 4;
478 gst_adapter_flush (mAdapter, len);
480 return gst_adapter_take_buffer (mAdapter, gst_adapter_available (mAdapter));
484 #define MAX_INTERNAL_BUFFER (8*1024)
493 StreamListener::OnDataAvailable(nsIRequest *req,
nsISupports *ctxt,
494 nsIInputStream *stream,
497 GST_DEBUG_OBJECT (mSrc,
"%p::OnDataAvailable called: [count=%u, offset=%u]",
498 this, count, offset);
501 PRUint32 bytesRead=0;
504 GstBuffer *buf = gst_buffer_new_and_alloc (count);
506 rv = stream->Read((
char *)GST_BUFFER_DATA (buf), count, &bytesRead);
508 GST_DEBUG_OBJECT (mSrc,
"stream->Read failed with rv=%x", rv);
512 GST_BUFFER_SIZE (buf) = bytesRead;
515 GST_DEBUG_OBJECT (mSrc,
"Scanning for headers");
518 buf = ScanForHeaders (buf);
528 len = GST_BUFFER_SIZE (buf);
531 gst_buffer_set_caps (buf, mSrc->
icy_caps);
536 g_queue_push_tail (mSrc->
queue, buf);
537 GST_DEBUG_OBJECT (mSrc,
"Pushed %d byte buffer onto queue (now %d bytes)",
555 GST_WARNING_OBJECT (mSrc,
"Trying to suspend while already suspended, "
563 nsCOMPtr<nsIRequest> request(do_QueryInterface(mSrc->
channel));
565 GST_DEBUG_OBJECT (mSrc,
"Suspending request, reading too fast!");
580 static const GInterfaceInfo urihandler_info = {
586 g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info);
595 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
597 gst_element_class_add_pad_template (element_class,
598 gst_static_pad_template_get (&srctemplate));
606 GObjectClass *gobject_class;
607 GstBaseSrcClass *gstbasesrc_class;
608 GstPushSrcClass *gstpushsrc_class;
610 gobject_class = (GObjectClass *) klass;
611 gstbasesrc_class = (GstBaseSrcClass *) klass;
612 gstpushsrc_class = (GstPushSrcClass *) klass;
618 g_object_class_install_property
620 g_param_spec_string (
"location",
"Location",
621 "Location to read from",
"", (GParamFlags)G_PARAM_READWRITE));
623 g_object_class_install_property
625 g_param_spec_boolean (
"iradio-mode",
"iradio-mode",
626 "Enable reading of shoutcast/icecast metadata",
627 FALSE, (GParamFlags)G_PARAM_READWRITE));
632 gstbasesrc_class->is_seekable =
636 gstbasesrc_class->unlock_stop =
641 GST_DEBUG_CATEGORY_INIT (mozillasrc_debug,
"mozillasrc", 0,
647 gst_buffer_unref ((GstBuffer *)data);
653 GST_DEBUG_OBJECT (src,
"Flushing input queue");
658 g_queue_free (src->
queue);
659 src->
queue = g_queue_new ();
694 src->
queue = g_queue_new ();
704 GST_DEBUG_OBJECT (gobject,
"Finalizing mozillasrc");
711 g_queue_free(src->
queue);
713 G_OBJECT_CLASS (parent_class)->finalize (gobject);
718 const GValue * value, GParamSpec * pspec)
725 src->
location = g_value_dup_string (value);
734 G_OBJECT_WARN_INVALID_PROPERTY_ID (
object, prop_id, pspec);
741 GValue * value, GParamSpec * pspec)
748 g_value_set_string (value, src->
location);
757 G_OBJECT_WARN_INVALID_PROPERTY_ID (
object, prop_id, pspec);
769 GST_DEBUG_OBJECT (src,
"unlock");
784 GST_DEBUG_OBJECT (src,
"unlock_stop");
800 basesrc = GST_BASE_SRC_CAST (psrc);
802 if (G_UNLIKELY (src->
eos)) {
803 GST_DEBUG_OBJECT (src,
"Create called at EOS");
804 return GST_FLOW_UNEXPECTED;
808 GST_DEBUG_OBJECT (src,
"Create called while flushing");
809 return GST_FLOW_WRONG_STATE;
815 GST_DEBUG_OBJECT (src,
"Queue has %d bytes", src->
queue_size);
819 if (g_queue_is_empty (src->
queue)) {
820 GST_DEBUG_OBJECT (src,
"Queue is empty; waiting");
823 nsCOMPtr<nsIRunnable> resume_event =
new ResumeEvent (src);
824 NS_DispatchToMainThread (resume_event);
826 GST_DEBUG_OBJECT (src,
"Starting wait");
828 GST_DEBUG_OBJECT (src,
"Wait done, we should have a buffer now");
832 if (g_queue_is_empty (src->
queue)) {
833 GST_DEBUG_OBJECT (src,
"Still no buffer; bailing");
834 ret = GST_FLOW_UNEXPECTED;
840 *outbuf = (GstBuffer *)g_queue_pop_head (src->
queue);
846 GST_DEBUG_OBJECT (src,
"Popped %d byte buffer from queue",
847 GST_BUFFER_SIZE (*outbuf));
859 nsCOMPtr<nsIRequest> request(do_QueryInterface(src->
channel));
863 GST_DEBUG_OBJECT (src,
"Resuming request for cancel");
872 GST_DEBUG_OBJECT (src,
"Cancelling request");
885 rv = NS_NewChannel(getter_AddRefs(src->
channel), src->
uri,
886 nsnull, nsnull, nsnull,
887 nsIRequest::LOAD_BYPASS_CACHE | nsIRequest::INHIBIT_CACHING);
888 if (NS_FAILED (rv)) {
889 GST_WARNING_OBJECT (src,
"Failed to create channel for %s", src->
location);
893 nsCOMPtr<nsIInterfaceRequestor> requestor = do_QueryInterface (listener);
894 src->
channel->SetNotificationCallbacks(requestor);
896 if (segment && segment->format == GST_FORMAT_BYTES && segment->start > 0) {
897 nsCOMPtr<nsIResumableChannel> resumable(do_QueryInterface(src->
channel));
899 GST_DEBUG_OBJECT (src,
"Trying to resume at %d bytes", segment->start);
901 rv = resumable->ResumeAt(segment->start, EmptyCString());
904 GST_WARNING_OBJECT (src,
905 "Failed to resume channel at non-zero offsets");
911 nsCOMPtr<nsIHttpChannel> httpchannel(do_QueryInterface(src->
channel));
914 NS_NAMED_LITERAL_CSTRING (useragent,
"User-Agent");
915 NS_NAMED_LITERAL_CSTRING (mozilla,
"Mozilla");
916 NS_NAMED_LITERAL_CSTRING (notmozilla,
"NotMoz");
921 rv = httpchannel->SetRequestHeader(
922 NS_LITERAL_CSTRING(
"icy-metadata"),
923 NS_LITERAL_CSTRING(
"1"),
926 GST_WARNING_OBJECT (src,
927 "Failed to set icy-metadata header on channel");
934 rv = httpchannel->GetRequestHeader (useragent, agent);
936 if (NS_SUCCEEDED (rv)) {
938 GST_DEBUG_OBJECT (src,
"Default User-Agent is '%s'",
939 agent.BeginReading());
941 offset = agent.Find(mozilla);
943 agent.Replace(offset, mozilla.Length(), notmozilla);
946 GST_DEBUG_OBJECT (src,
"Actual User-Agent is '%s'",
947 agent.BeginReading());
949 rv = httpchannel->SetRequestHeader(useragent, agent, PR_FALSE);
951 GST_WARNING_OBJECT (src,
"Failed to set user agent on channel");
955 rv = src->
channel->AsyncOpen(listener, nsnull);
956 if (NS_FAILED (rv)) {
957 GST_WARNING_OBJECT (src,
"Failed to open channel for %s", src->
location);
971 GST_WARNING_OBJECT (src,
"No location set");
975 rv = NS_NewURI(getter_AddRefs(src->
uri), src->
location);
976 if (NS_FAILED (rv)) {
977 GST_WARNING_OBJECT (src,
"Failed to create URI from %s", src->
location);
983 GST_DEBUG_OBJECT (src,
"Started request");
987 GST_ELEMENT_ERROR (src, LIBRARY, INIT,
988 (NULL), (
"Failed to initialise mozilla to fetch uri %s",
999 GST_INFO_OBJECT (src,
"Stop(): shutting down connection");
1002 nsCOMPtr<nsIRequest> request(do_QueryInterface(src->
channel));
1004 GST_DEBUG_OBJECT (src,
"Cancelling request");
1053 GST_INFO_OBJECT (src,
"is_seekable FALSE; failing seek immediately");
1061 GST_INFO_OBJECT (src,
"New request for seek initiated");
1065 GST_WARNING_OBJECT (src,
"Creating new request for seek failed");
1083 if (_supported_protocols != NULL)
1086 int numprotocols = 0;
1088 nsCOMPtr<nsIComponentRegistrar>
registrar;
1089 rv = NS_GetComponentRegistrar(getter_AddRefs(registrar));
1090 NS_ENSURE_SUCCESS(rv, NULL);
1092 nsCOMPtr<nsISimpleEnumerator> simpleEnumerator;
1093 rv = registrar->EnumerateContractIDs(getter_AddRefs(simpleEnumerator));
1094 NS_ENSURE_SUCCESS(rv, NULL);
1097 nsCOMPtr<nsISupports>
element;
1098 PRBool more = PR_FALSE;
1099 while(NS_SUCCEEDED(simpleEnumerator->HasMoreElements(&more)) && more) {
1101 rv = simpleEnumerator->GetNext(getter_AddRefs(element));
1102 NS_ENSURE_SUCCESS(rv, NULL);
1104 nsCOMPtr<nsISupportsCString> contractString =
1105 do_QueryInterface(element, &rv);
1107 NS_WARNING(
"QueryInterface failed");
1112 rv = contractString->GetData(contractID);
1114 NS_WARNING(
"GetData failed");
1118 NS_NAMED_LITERAL_CSTRING(prefix, NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX);
1120 if (!StringBeginsWith(contractID, prefix))
1124 scheme.Cut(0, prefix.Length());
1126 if (scheme.Equals(NS_LITERAL_CSTRING(
"file")))
1129 GST_DEBUG (
"Skipping file scheme");
1133 GST_DEBUG (
"Adding scheme '%s'", scheme.BeginReading());
1137 _supported_protocols = (gchar **)g_realloc (_supported_protocols,
1138 sizeof(gchar *) * (numprotocols + 1));
1139 _supported_protocols[numprotocols - 1] = g_strdup (scheme.BeginReading());
1140 _supported_protocols[numprotocols] = NULL;
1146 static const gchar *
1159 GST_DEBUG_OBJECT (src,
"URI set to '%s'", uri);
1168 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
nsCOMPtr< nsIChannel > channel
GST_DEBUG_CATEGORY_STATIC(mozillasrc_debug)
static gboolean gst_mozilla_src_stop(GstBaseSrc *bsrc)
static const GstElementDetails gst_mozilla_src_details
static gboolean gst_mozilla_src_create_request(GstMozillaSrc *src, GstSegment *segment)
static GstStaticPadTemplate srctemplate
virtual ~StreamListener()
function succeeded(ch, cx, status, data)
static gboolean gst_mozilla_src_start(GstBaseSrc *bsrc)
sbDeviceFirmwareAutoCheckForUpdate prototype contractID
static const gchar * gst_mozilla_src_uri_get_uri(GstURIHandler *handler)
sbOSDControlService prototype QueryInterface
static void gst_mozilla_src_cancel_request(GstMozillaSrc *src)
NS_DECL_ISUPPORTS NS_DECL_NSIRUNNABLE ResumeEvent(GstMozillaSrc *src)
NS_DECL_ISUPPORTS NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER NS_DECL_NSIHTTPHEADERVISITOR NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIHTTPEVENTSINK StreamListener(GstMozillaSrc *aSrc)
static void gst_mozilla_src_clear(GstMozillaSrc *src)
static gchar ** gst_mozilla_src_uri_get_protocols(void)
GST_BOILERPLATE_FULL(GstMozillaSrc, gst_mozilla_src, GstPushSrc, GST_TYPE_PUSH_SRC, _urihandler_init)
static void gst_mozilla_src_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
static void gst_mozilla_src_class_init(GstMozillaSrcClass *klass)
static void gst_mozilla_src_flush(GstMozillaSrc *src)
static gboolean gst_mozilla_src_get_size(GstBaseSrc *bsrc, guint64 *size)
static GstFlowReturn gst_mozilla_src_create(GstPushSrc *psrc, GstBuffer **outbuf)
GstPushSrcClass parent_class
NS_IMPL_THREADSAFE_ISUPPORTS5(StreamListener, nsIRequestObserver, nsIStreamListener, nsIHttpHeaderVisitor, nsIInterfaceRequestor, nsIHttpEventSink) NS_IMETHODIMP StreamListener
static void gst_mozilla_src_base_init(gpointer g_class)
static gboolean gst_mozilla_src_do_seek(GstBaseSrc *bsrc, GstSegment *segment)
#define GST_MOZILLA_SRC(obj)
gboolean shoutcast_headers_read
static GstURIType gst_mozilla_src_uri_get_type(void)
static gboolean gst_mozilla_src_is_seekable(GstBaseSrc *bsrc)
static void gst_mozilla_src_uri_handler_init(gpointer g_iface, gpointer iface_data)
static void gst_mozilla_src_finalize(GObject *gobject)
NS_IMPL_THREADSAFE_ISUPPORTS1(ResumeEvent, nsIRunnable) NS_IMETHODIMP ResumeEvent
static gchar ** _supported_protocols
static gboolean gst_mozilla_src_uri_set_uri(GstURIHandler *handler, const gchar *uri)
static void gst_mozilla_src_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
const nsIInterfaceRequestor
#define MAX_INTERNAL_BUFFER
static void _urihandler_init(GType type)
static void unref_buffer(gpointer data, gpointer user_data)
gboolean is_shoutcast_server
static void gst_mozilla_src_init(GstMozillaSrc *src, GstMozillaSrcClass *g_class)
static gboolean gst_mozilla_src_unlock_stop(GstBaseSrc *psrc)
static gboolean gst_mozilla_src_unlock(GstBaseSrc *psrc)