sbGStreamerRTPStreamer.cpp
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2010 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
25 #include "sbGStreamerRTPStreamer.h"
26 
27 #include <sbIGStreamerService.h>
28 
29 #include <sbClassInfoUtils.h>
30 #include <nsServiceManagerUtils.h>
31 #include <nsThreadUtils.h>
32 #include <nsStringAPI.h>
33 #include <prlog.h>
34 
35 #include <sbVariantUtils.h>
36 
37 #include "gst/sdp/gstsdp.h"
38 #include "gst/sdp/gstsdpmessage.h"
39 
44 #ifdef PR_LOGGING
45 static PRLogModuleInfo* gGStreamerRTPStreamer = PR_NewLogModule("sbGStreamerRTPStreamer");
46 #define LOG(args) PR_LOG(gGStreamerRTPStreamer, PR_LOG_WARNING, args)
47 #define TRACE(args) PR_LOG(gGStreamerRTPStreamer, PR_LOG_DEBUG, args)
48 #else /* PR_LOGGING */
49 #define LOG(args) /* nothing */
50 #define TRACE(args) /* nothing */
51 #endif /* PR_LOGGING */
52 
58 
63 
64 NS_DECL_CLASSINFO(sbGstreamerRTPStreamer);
65 NS_IMPL_THREADSAFE_CI(sbGStreamerRTPStreamer);
66 
67 sbGStreamerRTPStreamer::sbGStreamerRTPStreamer() :
69  mSourceURI(nsnull),
70  mDestHost(nsnull),
71  mDestPort(0)
72 {
73 }
74 
75 sbGStreamerRTPStreamer::~sbGStreamerRTPStreamer()
76 {
77 }
78 
79 /* sbIGStreamerRTPStreamer interface implementation */
80 
81 NS_IMETHODIMP
82 sbGStreamerRTPStreamer::SetSourceURI(const nsAString& aSourceURI)
83 {
84  mSourceURI = aSourceURI;
85  return NS_OK;
86 }
87 
88 NS_IMETHODIMP
89 sbGStreamerRTPStreamer::GetSourceURI(nsAString& aSourceURI)
90 {
91  aSourceURI = mSourceURI;
92  return NS_OK;
93 }
94 
95 NS_IMETHODIMP
96 sbGStreamerRTPStreamer::SetDestHost(const nsAString& aDestHost)
97 {
98  mDestHost = aDestHost;
99  return NS_OK;
100 }
101 
102 NS_IMETHODIMP
103 sbGStreamerRTPStreamer::GetDestHost(nsAString& aDestHost)
104 {
105  aDestHost = mDestHost;
106  return NS_OK;
107 }
108 
109 NS_IMETHODIMP
110 sbGStreamerRTPStreamer::SetDestPort(PRInt32 aDestPort)
111 {
112  mDestPort = aDestPort;
113  return NS_OK;
114 }
115 
116 NS_IMETHODIMP
117 sbGStreamerRTPStreamer::GetDestPort(PRInt32 *aDestPort)
118 {
119  *aDestPort = mDestPort;
120  return NS_OK;
121 }
122 
123 nsresult
125 {
126  nsCString pipelineString = NS_ConvertUTF16toUTF8(mSourceURI);
127  // TODO: Figure out what format we want to use, or make configurable.
128 #if 1
129  pipelineString += NS_LITERAL_CSTRING(
130  " ! decodebin ! audioconvert ! audioresample ! vorbisenc ! "
131  "rtpvorbispay name=payloader ! multiudpsink name=udpsink");
132 #else
133  pipelineString += NS_LITERAL_CSTRING(
134  " ! decodebin ! audioconvert ! audioresample ! lame ! mp3parse ! "
135  "rtpmpapay name=payloader ! multiudpsink name=udpsink");
136 #endif
137 
138  GError *error = NULL;
139  nsresult rv;
140  GstElement *sink, *payloader;
141  GstPad *srcpad;
142  nsCString host;
143 
144  mPipeline = gst_parse_launch (pipelineString.BeginReading(), &error);
145 
146  if (!mPipeline) {
147  // TODO: Report the error more usefully using the GError
148  rv = NS_ERROR_FAILURE;
149  return rv;
150  }
151 
152  sink = gst_bin_get_by_name (GST_BIN (mPipeline), "udpsink");
153  // Add the host and port to stream to
154  host = NS_ConvertUTF16toUTF8(mDestHost);
155  g_signal_emit_by_name (sink, "add", host.BeginReading(), mDestPort);
156  gst_object_unref (sink);
157 
158  payloader = gst_bin_get_by_name (GST_BIN (mPipeline), "payloader");
159  srcpad = gst_element_get_pad (payloader, "src");
160  g_signal_connect (srcpad, "notify::caps", (GCallback) capsNotifyHelper, this);
161  gst_object_unref (srcpad);
162  gst_object_unref (payloader);
163 
165 
166  return NS_OK;
167 }
168 
169 /* static */ void
170 sbGStreamerRTPStreamer::capsNotifyHelper(GObject* obj, GParamSpec* pspec,
171  sbGStreamerRTPStreamer *streamer)
172 {
173  GstPad *pad = GST_PAD(obj);
174  GstCaps *caps = gst_pad_get_negotiated_caps(pad);
175 
176  if (caps) {
177  streamer->OnCapsSet(caps);
178  gst_caps_unref (caps);
179  }
180 }
181 
182 void
184 {
185  GstSDPMessage *sdp;
186  GstSDPMedia *media;
187  GstStructure *s;
188  const gchar *capsstr, *encoding_name, *encoding_params;
189  int pt, rate;
190  gchar *tmp;
191  const char *stdproperties[] = {"media", "payload", "clock-rate",
192  "encoding-name", "encoding-params", "ssrc", "clock-base",
193  "seqnum-base"};
194 
195  // Following is largely based on rtsp-sdp.c from gst-rtsp-server
196 
197  gst_sdp_message_new (&sdp);
198  // SDP is always version 0
199  gst_sdp_message_set_version (sdp, "0");
200  gst_sdp_message_set_origin (sdp,
201  "-", // Username, optional
202  "1234567890", // Session ID, recommended that this is an NTP timestamp
203  "1", // Version, hard code to 1 for now
204  "IN", // Network type, always IN (Internet)
205  "IP4", // Address type, for now hardcode to IPv4
206  "127.0.0.1"); // Actual origin address. TODO: Set accurately?
207  gst_sdp_message_set_session_name (sdp, "Songbird RTP Stream");
208  gst_sdp_message_set_information (sdp, "Streaming from Songbird");
209  gst_sdp_message_add_time (sdp, "0", "0", NULL);
210  gst_sdp_message_add_attribute (sdp, "tool", "songbird");
211 
212  // TODO: Add a 'range' attribute with the correct info about the media
213  // item; that way we can EOS at the appropriate time (requires more recent
214  // gstreamer than we have though - backport patches?)
215  // To do that, we'll want to change sourceURI to be a mediaitem, I think.
216 
217  gst_sdp_media_new (&media);
218  s = gst_caps_get_structure (caps, 0);
219  // Take media type and payload type number from the caps.
220  capsstr = gst_structure_get_string (s, "media");
221  gst_sdp_media_set_media (media, capsstr);
222 
223  gst_structure_get_int (s, "payload", &pt);
224  tmp = g_strdup_printf("%d", pt);
225  gst_sdp_media_add_format (media, tmp);
226  g_free (tmp);
227 
228  gst_sdp_media_set_port_info (media, mDestPort, 1);
229  gst_sdp_media_set_proto (media, "RTP/AVP");
230 
231  // Connection info. Since we're not using multicast, we don't actually know
232  // what IP the client will be receiving packets from. So, we specify the
233  // address as 0.0.0.0, or INADDR_ANY
234  gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
235 
236  gst_structure_get_int (s, "clock-rate", &rate);
237  encoding_name = gst_structure_get_string (s, "encoding-name");
238  encoding_params = gst_structure_get_string (s, "encoding-params");
239  if (encoding_params)
240  tmp = g_strdup_printf ("%d %s/%d/%s", pt, encoding_name, rate,
241  encoding_params);
242  else
243  tmp = g_strdup_printf ("%d %s/%d", pt, encoding_name, rate);
244  gst_sdp_media_add_attribute (media, "rtpmap", tmp);
245  g_free (tmp);
246 
247  // Generate fmtp (format-specific configuration info) attribute
248  int fields = gst_structure_n_fields (s);
249  GString *fmtp = g_string_new ("");
250  g_string_append_printf (fmtp, "%d ", pt);
251  bool first = true;
252  for (int i = 0; i < fields; i++)
253  {
254  bool skip = false;
255  const gchar *fieldname = gst_structure_nth_field_name (s, i);
256  const gchar *fieldval;
257  for (unsigned int j = 0;
258  j < sizeof (stdproperties)/sizeof(*stdproperties); j++)
259  {
260  if (!strcmp (fieldname, stdproperties[i]))
261  skip = true;
262  }
263  if (!skip && (fieldval = gst_structure_get_string (s, fieldname)))
264  {
265  if (!first) {
266  g_string_append_printf (fmtp, ";");
267  }
268  first = false;
269  g_string_append_printf (fmtp, "%s=%s", fieldname, fieldval);
270  }
271  }
272 
273  if (!first)
274  {
275  gst_sdp_media_add_attribute (media, "fmtp", fmtp->str);
276  }
277  g_string_free (fmtp, TRUE);
278 
279  gst_sdp_message_add_media (sdp, media);
280  gst_sdp_media_free (media);
281 
282  gchar *text = gst_sdp_message_as_text (sdp);
283  nsCString sdptext(text);
284  g_free (text);
285 
286  gst_sdp_message_free (sdp);
287 
288  // Now send an event to let listeners know about the SDP.
289  nsCOMPtr<nsIVariant> sdpVariant = sbNewVariant(sdptext).get();
291  sdpVariant);
292 }
293 
294 
295 
return NS_OK
NS_IMPL_CI_INTERFACE_GETTER3(sbMediaListEnumeratorWrapper, sbIMediaListEnumeratorWrapper, nsISimpleEnumerator, nsIClassInfo)
nsIVariant * get() const
Songbird Variant Utility Definitions.
function skip(aMessage)
NS_IMPL_THREADSAFE_CI(sbGStreamerRTPStreamer)
void DispatchMediacoreEvent(unsigned long type, nsIVariant *aData=NULL, sbIMediacoreError *aError=NULL)
NS_IMPL_THREADSAFE_ISUPPORTS4(sbGStreamerRTPStreamer, sbIGStreamerPipeline, sbIGStreamerRTPStreamer, sbIMediacoreEventTarget, nsIClassInfo) NS_IMPL_CI_INTERFACE_GETTER3(sbGStreamerRTPStreamer
void SetPipelineOp(GStreamer::pipelineOp_t aPipelineOp)
NS_DECL_CLASSINFO(sbGstreamerRTPStreamer)
const unsigned long EVENT_SDP_AVAILABLE
_getSelectedPageStyle s i
return first