diff --git a/ChangeLog b/ChangeLog index 611670653b..861bdebc52 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,19 @@ +2006-09-21 Wim Taymans + + * gst/rtp/README: + Update README with some examples. + + * gst/rtp/gstrtpmp4gpay.c: (gst_rtp_mp4g_pay_init), + (gst_rtp_mp4g_pay_finalize), (gst_rtp_mp4g_pay_parse_audio_config), + (gst_rtp_mp4g_pay_parse_video_config), (gst_rtp_mp4g_pay_new_caps), + (gst_rtp_mp4g_pay_setcaps): + * gst/rtp/gstrtpmp4gpay.h: + Make optional RTP parameters of type STRING, as required by the + application/x-rtp caps specification. + 2006-09-20 Philippe Kalaf - * gst/rtp/gstrtph263pdepay.c: + * gst/rtp/gstrtph263pdepay.c: * gst/rtp/gstrtph263ppay.c: Correctly calculate size of each H263+ RTP buffer taking into account MTU and RTP header. diff --git a/gst/rtp/README b/gst/rtp/README index 146c55faaa..129efb97fd 100644 --- a/gst/rtp/README +++ b/gst/rtp/README @@ -29,7 +29,7 @@ The following fields can or must (*) be specified in the structure: clock-base: (uint) [0 - MAXINT] The RTP time representing time 0 - seqnum-base: + seqnum-base: (uint) [0 - MAXINT] The RTP sequence number representing the first rtp packet encoding-name: (String) ANY @@ -76,19 +76,96 @@ The following fields can or must (*) be specified in the structure: possible. -TODO ----- +usage with UDP +-------------- -- implement packing up to the MTU. -- discont events in the case of packet loss -- figure out the clocking. -- implement various RFCs dealing with different payload types. - (as modules?) -- Throw-out the the caps-nego & other session control things to the - Application Developer( App ), by turning rtcp work into, signals - in gstrtpsend & props/args in gstrtprecv. - The App would then be free to use any sort of session control - protocal like RTSP.( done ) +To correctly and completely use the RTP payloaders on the sender and the +receiver you need to write an application. It is not possible to write a full +blown RTP server with a single gst-launch line. + +That said, it is possible to do something functional with a few gst-launch +lines. The biggest problem when constructing a correct gst-launch line lies on +the receiver end. + +The receiver needs to know about the type of the RTP data along with a set of +RTP configuration parameters. This information is usually transmitted to the +client using some sort of session description language (SDP) over some reliable +channel (HTTP/RTSP/...). + +All of the required parameters to connect and use the RTP session on the +server can be found in the caps on the server end. The client receives this +information in some way (caps are converted to and from SDP, as explained above, +for example). + +Some gst-launch lines: + + gst-launch-0.10 -v videotestsrc ! ffenc_h263p ! rtph263ppay ! udpsink + + Setting pipeline to PAUSED ... + /pipeline0/videotestsrc0.src: caps = video/x-raw-yuv, format=(fourcc)I420, + width=(int)320, height=(int)240, framerate=(fraction)30/1 + Pipeline is PREROLLING ... + .... + /pipeline0/udpsink0.sink: caps = application/x-rtp, media=(string)video, + payload=(int)96, clock-rate=(int)90000, encoding-name=(string)H263-1998, + ssrc=(guint)527842345, clock-base=(guint)1150776941, seqnum-base=(guint)30982 + .... + Pipeline is PREROLLED ... + Setting pipeline to PLAYING ... + New clock: GstSystemClock + + Write down the caps on the udpsink and set them as the caps of the UDP + receiver: + + gst-launch-0.10 -v udpsrc caps="application/x-rtp, media=(string)video, + payload=(int)96, clock-rate=(int)90000, encoding-name=(string)H263-1998, + ssrc=(guint)527842345, clock-base=(guint)1150776941, seqnum-base=(guint)30982" + ! rtph263pdepay ! ffdec_h263 ! xvimagesink sync=false + + The receiver now displays an h263 image. Note that the sync parameter on + xvimagesink needs to be FALSE because we do not have an RTP session manager + that controls the synchronisation in this pipeline. + + Stream a quicktime file with mpeg4 video and AAC audio on port 5000 and port + 5002. + + gst-launch-0.10 -v filesrc location=~/data/sincity.mp4 ! qtdemux name=d ! queue ! rtpmp4vpay ! udpsink port=5000 + d. ! queue ! rtpmp4gpay ! udpsink port=5002 + .... + /pipeline0/udpsink0.sink: caps = application/x-rtp, media=(string)video, + payload=(int)96, clock-rate=(int)90000, encoding-name=(string)MP4V-ES, + ssrc=(guint)1162703703, clock-base=(guint)816135835, seqnum-base=(guint)9294, + profile-level-id=(string)3, config=(string)000001b003000001b50900000100000001200086c5d4c307d314043c1463000001b25876694430303334 + /pipeline0/udpsink1.sink: caps = application/x-rtp, media=(string)audio, + payload=(int)96, clock-rate=(int)44100, encoding-name=(string)mpeg4-generic, + ssrc=(guint)3246149898, clock-base=(guint)4134514058, seqnum-base=(guint)57633, + encoding-params=(string)2, streamtype=(string)5, profile-level-id=(string)1, + mode=(string)AAC-hbr, config=(string)1210, sizelength=(string)13, + indexlength=(string)3, indexdeltalength=(string)3 + .... + + Again copy the caps on both sinks to the receiver launch line + + gst-launch + udpsrc port=5000 caps="application/x-rtp, media=(string)video, payload=(int)96, + clock-rate=(int)90000, encoding-name=(string)MP4V-ES, ssrc=(guint)1162703703, + clock-base=(guint)816135835, seqnum-base=(guint)9294, profile-level-id=(string)3, + config=(string)000001b003000001b50900000100000001200086c5d4c307d314043c1463000001b25876694430303334" + ! rtpmp4vdepay ! ffdec_mpeg4 ! xvimagesink sync=false + udpsrc port=5002 caps="application/x-rtp, media=(string)audio, payload=(int)96, + clock-rate=(int)44100, encoding-name=(string)mpeg4-generic, ssrc=(guint)3246149898, + clock-base=(guint)4134514058, seqnum-base=(guint)57633, encoding-params=(string)2, + streamtype=(string)5, profile-level-id=(string)1, mode=(string)AAC-hbr, + config=(string)1210, sizelength=(string)13, indexlength=(string)3, + indexdeltalength=(string)3" + ! rtpmp4gdepay ! faad ! alsasink sync=false + + The caps on the udpsinks can be retrieved when the server pipeline prerolled to + PAUSED. + + The caps on the receiver side can be set on the UDP source elements when the + pipeline went to PAUSED. In that state no data is received from the UDP sources + as they are live sources and only produce data in PLAYING. Relevant RFCs diff --git a/gst/rtp/gstrtpmp4gpay.c b/gst/rtp/gstrtpmp4gpay.c index 3c8d804f92..42fae2ef07 100644 --- a/gst/rtp/gstrtpmp4gpay.c +++ b/gst/rtp/gstrtpmp4gpay.c @@ -56,24 +56,25 @@ GST_STATIC_PAD_TEMPLATE ("src", "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"mpeg4-generic\", " /* required string params */ - "streamtype = (int) { \"4\", \"5\" }, " /* 4 = video, 5 = audio */ - "profile-level-id = (int) [1,MAX], " + "streamtype = (string) { \"4\", \"5\" }, " /* 4 = video, 5 = audio */ + /* "profile-level-id = (string) [1,MAX], " */ /* "config = (string) [1,MAX]" */ - "mode = (string) { \"generic\", \"CELP-cbr\", \"CELP-vbr\", \"AAC-lbr\", \"AAC-hbr\" }, " + "mode = (string) { \"generic\", \"CELP-cbr\", \"CELP-vbr\", \"AAC-lbr\", \"AAC-hbr\" } " /* Optional general parameters */ - "objecttype = (int) [1,MAX], " "constantsize = (int) [1,MAX], " /* constant size of each AU */ - "constantduration = (int) [1,MAX], " /* constant duration of each AU */ - "maxdisplacement = (int) [1,MAX], " - "de-interleavebuffersize = (int) [1,MAX], " + /* "objecttype = (string) [1,MAX], " */ + /* "constantsize = (string) [1,MAX], " *//* constant size of each AU */ + /* "constantduration = (string) [1,MAX], " *//* constant duration of each AU */ + /* "maxdisplacement = (string) [1,MAX], " */ + /* "de-interleavebuffersize = (string) [1,MAX], " */ /* Optional configuration parameters */ - "sizelength = (int) [1, 16], " /* max 16 bits, should be enough... */ - "indexlength = (int) [1, 8], " - "indexdeltalength = (int) [1, 8], " - "ctsdeltalength = (int) [1, 64], " - "dtsdeltalength = (int) [1, 64], " - "randomaccessindication = (int) {0, 1}, " - "streamstateindication = (int) [0, 64], " - "auxiliarydatasizelength = (int) [0, 64]") + /* "sizelength = (string) [1, 16], " *//* max 16 bits, should be enough... */ + /* "indexlength = (string) [1, 8], " */ + /* "indexdeltalength = (string) [1, 8], " */ + /* "ctsdeltalength = (string) [1, 64], " */ + /* "dtsdeltalength = (string) [1, 64], " */ + /* "randomaccessindication = (string) {0, 1}, " */ + /* "streamstateindication = (string) [0, 64], " */ + /* "auxiliarydatasizelength = (string) [0, 64]" */ ) ); enum @@ -167,7 +168,7 @@ gst_rtp_mp4g_pay_init (GstRtpMP4GPay * rtpmp4gpay) { rtpmp4gpay->adapter = gst_adapter_new (); rtpmp4gpay->rate = 90000; - rtpmp4gpay->profile = 1; + rtpmp4gpay->profile = g_strdup ("1"); rtpmp4gpay->mode = ""; } @@ -180,6 +181,8 @@ gst_rtp_mp4g_pay_finalize (GObject * object) g_object_unref (rtpmp4gpay->adapter); rtpmp4gpay->adapter = NULL; + g_free (rtpmp4gpay->params); + rtpmp4gpay->params = NULL; G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -232,13 +235,15 @@ gst_rtp_mp4g_pay_parse_audio_config (GstRtpMP4GPay * rtpmp4gpay, rtpmp4gpay->rate = sampling_table[samplingIdx]; } /* extra rtp params contain the number of channels */ - rtpmp4gpay->params = channelCfg; + g_free (rtpmp4gpay->params); + rtpmp4gpay->params = g_strdup_printf ("%d", channelCfg); /* audio stream type */ - rtpmp4gpay->streamtype = 5; + rtpmp4gpay->streamtype = "5"; /* mode */ rtpmp4gpay->mode = "AAC-hbr"; /* profile (should be 1) */ - rtpmp4gpay->profile = objectType - 1; + g_free (rtpmp4gpay->profile); + rtpmp4gpay->profile = g_strdup_printf ("%d", objectType - 1); GST_DEBUG_OBJECT (rtpmp4gpay, "objectType: %d, samplingIdx: %d (%d), channelCfg: %d", objectType, @@ -290,25 +295,27 @@ gst_rtp_mp4g_pay_parse_video_config (GstRtpMP4GPay * rtpmp4gpay, goto too_short; code = GST_READ_UINT32_BE (data); + + g_free (rtpmp4gpay->profile); if (code == VOS_STARTCODE) { /* get profile */ - rtpmp4gpay->profile = data[4]; + rtpmp4gpay->profile = g_strdup_printf ("%d", (gint) data[4]); } else { GST_ELEMENT_WARNING (rtpmp4gpay, STREAM, FORMAT, - (NULL), ("profile not found in config string")); - rtpmp4gpay->profile = 1; + (NULL), ("profile not found in config string, assuming \'1\'")); + rtpmp4gpay->profile = g_strdup ("1"); } /* fixed rate */ rtpmp4gpay->rate = 90000; /* video stream type */ - rtpmp4gpay->streamtype = 4; + rtpmp4gpay->streamtype = "4"; /* no params for video */ - rtpmp4gpay->params = 0; + rtpmp4gpay->params = NULL; /* mode */ rtpmp4gpay->mode = "generic"; - GST_LOG_OBJECT (rtpmp4gpay, "profile %d", rtpmp4gpay->profile); + GST_LOG_OBJECT (rtpmp4gpay, "profile %s", rtpmp4gpay->profile); return TRUE; @@ -327,14 +334,14 @@ gst_rtp_mp4g_pay_new_caps (GstRtpMP4GPay * rtpmp4gpay) gchar *config; GValue v = { 0 }; -#define MP4GCAPS \ - "streamtype", G_TYPE_INT, rtpmp4gpay->streamtype, \ - "profile-level-id", G_TYPE_INT, rtpmp4gpay->profile, \ - "mode", G_TYPE_STRING, rtpmp4gpay->mode, \ - "config", G_TYPE_STRING, config, \ - "sizelength", G_TYPE_INT, 13, \ - "indexlength", G_TYPE_INT, 3, \ - "indexdeltalength", G_TYPE_INT, 3, \ +#define MP4GCAPS \ + "streamtype", G_TYPE_STRING, rtpmp4gpay->streamtype, \ + "profile-level-id", G_TYPE_STRING, rtpmp4gpay->profile, \ + "mode", G_TYPE_STRING, rtpmp4gpay->mode, \ + "config", G_TYPE_STRING, config, \ + "sizelength", G_TYPE_STRING, "13", \ + "indexlength", G_TYPE_STRING, "3", \ + "indexdeltalength", G_TYPE_STRING, "3", \ NULL g_value_init (&v, GST_TYPE_BUFFER); @@ -344,7 +351,7 @@ gst_rtp_mp4g_pay_new_caps (GstRtpMP4GPay * rtpmp4gpay) /* hmm, silly */ if (rtpmp4gpay->params) { gst_basertppayload_set_outcaps (GST_BASE_RTP_PAYLOAD (rtpmp4gpay), - "encoding-params", G_TYPE_INT, rtpmp4gpay->params, MP4GCAPS); + "encoding-params", G_TYPE_STRING, rtpmp4gpay->params, MP4GCAPS); } else { gst_basertppayload_set_outcaps (GST_BASE_RTP_PAYLOAD (rtpmp4gpay), MP4GCAPS); diff --git a/gst/rtp/gstrtpmp4gpay.h b/gst/rtp/gstrtpmp4gpay.h index bb0bb8fd69..b1557f5a55 100644 --- a/gst/rtp/gstrtpmp4gpay.h +++ b/gst/rtp/gstrtpmp4gpay.h @@ -49,9 +49,9 @@ struct _GstRtpMP4GPay GstClockTime duration; gint rate; - gint params; - gint profile; - gint streamtype; + gchar *params; + gchar *profile; + const gchar *streamtype; const gchar *mode; GstBuffer *config; };