diff options
Diffstat (limited to 'gst')
-rw-r--r-- | gst/avi/gstavidemux.c | 34 | ||||
-rw-r--r-- | gst/law/alaw-decode.c | 63 | ||||
-rw-r--r-- | gst/law/alaw-encode.c | 22 | ||||
-rw-r--r-- | gst/law/mulaw-decode.c | 58 | ||||
-rw-r--r-- | gst/law/mulaw-encode.c | 22 | ||||
-rw-r--r-- | gst/matroska/matroska-demux.c | 6 | ||||
-rw-r--r-- | gst/matroska/matroska-mux.c | 3 | ||||
-rw-r--r-- | gst/qtdemux/qtdemux.c | 5 | ||||
-rw-r--r-- | gst/qtdemux/qtdemux_fourcc.h | 1 | ||||
-rw-r--r-- | gst/replaygain/gstrgvolume.c | 14 | ||||
-rw-r--r-- | gst/rtp/gstrtph264pay.c | 128 | ||||
-rw-r--r-- | gst/rtp/gstrtpvrawdepay.c | 10 | ||||
-rw-r--r-- | gst/rtp/gstrtpvrawpay.c | 39 | ||||
-rw-r--r-- | gst/rtsp/gstrtspext.c | 17 | ||||
-rw-r--r-- | gst/rtsp/gstrtspext.h | 1 | ||||
-rw-r--r-- | gst/rtsp/gstrtspsrc.c | 90 | ||||
-rw-r--r-- | gst/rtsp/gstrtspsrc.h | 1 | ||||
-rw-r--r-- | gst/udp/gstudpnetutils.c | 16 | ||||
-rw-r--r-- | gst/udp/gstudpsrc.c | 78 | ||||
-rw-r--r-- | gst/wavparse/gstwavparse.c | 286 |
20 files changed, 705 insertions, 189 deletions
diff --git a/gst/avi/gstavidemux.c b/gst/avi/gstavidemux.c index 7a3166cf..adea05fd 100644 --- a/gst/avi/gstavidemux.c +++ b/gst/avi/gstavidemux.c @@ -619,9 +619,13 @@ gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: - /* handle seeking */ - res = gst_avi_demux_handle_seek (avi, pad, event); - gst_event_unref (event); + /* handle seeking only in pull mode */ + if (!avi->streaming) { + res = gst_avi_demux_handle_seek (avi, pad, event); + gst_event_unref (event); + } else { + res = gst_pad_event_default (pad, event); + } break; case GST_EVENT_QOS: case GST_EVENT_NAVIGATION: @@ -1707,7 +1711,7 @@ gst_avi_demux_parse_odml (GstAviDemux * avi, GstBuffer * buf) goto next; } _dmlh = (gst_riff_dmlh *) GST_BUFFER_DATA (sub); - dmlh.totalframes = GUINT32_FROM_LE (_dmlh->totalframes); + dmlh.totalframes = GST_READ_UINT32_LE (&_dmlh->totalframes); GST_INFO_OBJECT (avi, "dmlh tag found:"); GST_INFO_OBJECT (avi, " totalframes: %u", dmlh.totalframes); @@ -1799,10 +1803,10 @@ gst_avi_demux_parse_index (GstAviDemux * avi, GstFormat format; _entry = &((gst_riff_index_entry *) data)[i]; - entry.id = GUINT32_FROM_LE (_entry->id); - entry.offset = GUINT32_FROM_LE (_entry->offset); - entry.flags = GUINT32_FROM_LE (_entry->flags); - entry.size = GUINT32_FROM_LE (_entry->size); + entry.id = GST_READ_UINT32_LE (&_entry->id); + entry.offset = GST_READ_UINT32_LE (&_entry->offset); + entry.flags = GST_READ_UINT32_LE (&_entry->flags); + entry.size = GST_READ_UINT32_LE (&_entry->size); target = &entries[n]; if (entry.id == GST_RIFF_rec || entry.id == 0 || @@ -3599,7 +3603,7 @@ static GstBuffer * gst_avi_demux_invert (avi_stream_context * stream, GstBuffer * buf) { GstStructure *s; - gint y, h = stream->strf.vids->height; + gint y, w, h; gint bpp, stride; guint8 *tmp = NULL; @@ -3616,7 +3620,14 @@ gst_avi_demux_invert (avi_stream_context * stream, GstBuffer * buf) return buf; } - stride = stream->strf.vids->width * (bpp / 8); + if (stream->strf.vids == NULL) { + GST_WARNING ("Failed to retrieve vids for stream"); + return buf; + } + + h = stream->strf.vids->height; + w = stream->strf.vids->width; + stride = w * (bpp / 8); buf = gst_buffer_make_writable (buf); if (GST_BUFFER_SIZE (buf) < (stride * h)) { @@ -4239,6 +4250,7 @@ gst_avi_demux_sink_activate_pull (GstPad * sinkpad, gboolean active) if (active) { avi->segment_running = TRUE; + avi->streaming = FALSE; return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_avi_demux_loop, sinkpad); } else { @@ -4250,9 +4262,11 @@ gst_avi_demux_sink_activate_pull (GstPad * sinkpad, gboolean active) static gboolean gst_avi_demux_activate_push (GstPad * pad, gboolean active) { + GstAviDemux *avi = GST_AVI_DEMUX (GST_OBJECT_PARENT (pad)); if (active) { GST_DEBUG ("avi: activating push/chain function"); + avi->streaming = TRUE; } else { GST_DEBUG ("avi: deactivating push/chain function"); } diff --git a/gst/law/alaw-decode.c b/gst/law/alaw-decode.c index 9b990f7e..ac4c6df5 100644 --- a/gst/law/alaw-decode.c +++ b/gst/law/alaw-decode.c @@ -145,6 +145,57 @@ gst_alaw_dec_sink_setcaps (GstPad * pad, GstCaps * caps) return ret; } +static GstCaps * +gst_alaw_dec_getcaps (GstPad * pad) +{ + GstALawDec *alawdec; + GstPad *otherpad; + GstCaps *base_caps, *othercaps; + + alawdec = GST_ALAW_DEC (GST_PAD_PARENT (pad)); + + base_caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); + + if (pad == alawdec->srcpad) { + otherpad = alawdec->sinkpad; + } else { + otherpad = alawdec->srcpad; + } + othercaps = gst_pad_peer_get_caps (otherpad); + if (othercaps) { + GstStructure *structure; + const GValue *orate, *ochans; + const GValue *rate, *chans; + GValue irate = { 0 }; + GValue ichans = { 0 }; + + if (gst_caps_is_empty (othercaps) || gst_caps_is_any (othercaps)) + goto done; + + structure = gst_caps_get_structure (othercaps, 0); + orate = gst_structure_get_value (structure, "rate"); + ochans = gst_structure_get_value (structure, "channels"); + + structure = gst_caps_get_structure (base_caps, 0); + rate = gst_structure_get_value (structure, "rate"); + chans = gst_structure_get_value (structure, "channels"); + + if (orate) { + gst_value_intersect (&irate, orate, rate); + gst_structure_set_value (structure, "rate", &irate); + } + + if (ochans) { + gst_value_intersect (&ichans, ochans, chans); + gst_structure_set_value (structure, "channels", &ichans); + } + + done: + gst_caps_unref (othercaps); + } + return base_caps; +} + static void gst_alaw_dec_base_init (gpointer klass) { @@ -177,6 +228,8 @@ gst_alaw_dec_init (GstALawDec * alawdec, GstALawDecClass * klass) gst_pad_new_from_static_template (&alaw_dec_sink_factory, "sink"); gst_pad_set_setcaps_function (alawdec->sinkpad, GST_DEBUG_FUNCPTR (gst_alaw_dec_sink_setcaps)); + gst_pad_set_getcaps_function (alawdec->sinkpad, + GST_DEBUG_FUNCPTR (gst_alaw_dec_getcaps)); gst_pad_set_chain_function (alawdec->sinkpad, GST_DEBUG_FUNCPTR (gst_alaw_dec_chain)); gst_element_add_pad (GST_ELEMENT (alawdec), alawdec->sinkpad); @@ -184,6 +237,8 @@ gst_alaw_dec_init (GstALawDec * alawdec, GstALawDecClass * klass) alawdec->srcpad = gst_pad_new_from_static_template (&alaw_dec_src_factory, "src"); gst_pad_use_fixed_caps (alawdec->srcpad); + gst_pad_set_getcaps_function (alawdec->srcpad, + GST_DEBUG_FUNCPTR (gst_alaw_dec_getcaps)); gst_element_add_pad (GST_ELEMENT (alawdec), alawdec->srcpad); } @@ -238,14 +293,14 @@ gst_alaw_dec_chain (GstPad * pad, GstBuffer * buffer) not_negotiated: { gst_buffer_unref (buffer); - GST_ERROR_OBJECT (alawdec, "no format negotiated"); - ret = GST_FLOW_NOT_NEGOTIATED; - return ret; + GST_WARNING_OBJECT (alawdec, "no input format set: not-negotiated"); + return GST_FLOW_NOT_NEGOTIATED; } alloc_failed: { gst_buffer_unref (buffer); - GST_ERROR_OBJECT (alawdec, "pad alloc failed"); + GST_DEBUG_OBJECT (alawdec, "pad alloc failed, flow: %s", + gst_flow_get_name (ret)); return ret; } } diff --git a/gst/law/alaw-encode.c b/gst/law/alaw-encode.c index ba8587a2..b98d8e2e 100644 --- a/gst/law/alaw-encode.c +++ b/gst/law/alaw-encode.c @@ -318,8 +318,8 @@ gst_alaw_enc_getcaps (GstPad * pad) GstStructure *structure; const GValue *orate, *ochans; const GValue *rate, *chans; - GValue irate = { 0 }, ichans = { - 0}; + GValue irate = { 0 }; + GValue ichans = { 0 }; if (gst_caps_is_empty (othercaps) || gst_caps_is_any (othercaps)) goto done; @@ -327,22 +327,20 @@ gst_alaw_enc_getcaps (GstPad * pad) structure = gst_caps_get_structure (othercaps, 0); orate = gst_structure_get_value (structure, "rate"); ochans = gst_structure_get_value (structure, "channels"); - if (!orate || !ochans) - goto done; structure = gst_caps_get_structure (base_caps, 0); rate = gst_structure_get_value (structure, "rate"); chans = gst_structure_get_value (structure, "channels"); - if (!rate || !chans) - goto done; - gst_value_intersect (&irate, orate, rate); - gst_value_intersect (&ichans, ochans, chans); + if (orate) { + gst_value_intersect (&irate, orate, rate); + gst_structure_set_value (structure, "rate", &irate); + } - /* Set the samplerate/channels on the to-be-returned caps */ - structure = gst_caps_get_structure (base_caps, 0); - gst_structure_set_value (structure, "rate", &irate); - gst_structure_set_value (structure, "channels", &ichans); + if (ochans) { + gst_value_intersect (&ichans, ochans, chans); + gst_structure_set_value (structure, "channels", &ichans); + } done: gst_caps_unref (othercaps); diff --git a/gst/law/mulaw-decode.c b/gst/law/mulaw-decode.c index 42b208f6..831ef0fa 100644 --- a/gst/law/mulaw-decode.c +++ b/gst/law/mulaw-decode.c @@ -88,6 +88,57 @@ mulawdec_sink_setcaps (GstPad * pad, GstCaps * caps) return ret; } +static GstCaps * +mulawdec_getcaps (GstPad * pad) +{ + GstMuLawDec *mulawdec; + GstPad *otherpad; + GstCaps *base_caps, *othercaps; + + mulawdec = GST_MULAWDEC (GST_PAD_PARENT (pad)); + + base_caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); + + if (pad == mulawdec->srcpad) { + otherpad = mulawdec->sinkpad; + } else { + otherpad = mulawdec->srcpad; + } + othercaps = gst_pad_peer_get_caps (otherpad); + if (othercaps) { + GstStructure *structure; + const GValue *orate, *ochans; + const GValue *rate, *chans; + GValue irate = { 0 }; + GValue ichans = { 0 }; + + if (gst_caps_is_empty (othercaps) || gst_caps_is_any (othercaps)) + goto done; + + structure = gst_caps_get_structure (othercaps, 0); + orate = gst_structure_get_value (structure, "rate"); + ochans = gst_structure_get_value (structure, "channels"); + + structure = gst_caps_get_structure (base_caps, 0); + rate = gst_structure_get_value (structure, "rate"); + chans = gst_structure_get_value (structure, "channels"); + + if (orate) { + gst_value_intersect (&irate, orate, rate); + gst_structure_set_value (structure, "rate", &irate); + } + + if (ochans) { + gst_value_intersect (&ichans, ochans, chans); + gst_structure_set_value (structure, "channels", &ichans); + } + + done: + gst_caps_unref (othercaps); + } + return base_caps; +} + GType gst_mulawdec_get_type (void) { @@ -146,12 +197,14 @@ gst_mulawdec_init (GstMuLawDec * mulawdec) mulawdec->sinkpad = gst_pad_new_from_static_template (&mulaw_dec_sink_factory, "sink"); gst_pad_set_setcaps_function (mulawdec->sinkpad, mulawdec_sink_setcaps); + gst_pad_set_getcaps_function (mulawdec->sinkpad, mulawdec_getcaps); gst_pad_set_chain_function (mulawdec->sinkpad, gst_mulawdec_chain); gst_element_add_pad (GST_ELEMENT (mulawdec), mulawdec->sinkpad); mulawdec->srcpad = gst_pad_new_from_static_template (&mulaw_dec_src_factory, "src"); gst_pad_use_fixed_caps (mulawdec->srcpad); + gst_pad_set_getcaps_function (mulawdec->srcpad, mulawdec_getcaps); gst_element_add_pad (GST_ELEMENT (mulawdec), mulawdec->srcpad); } @@ -205,13 +258,14 @@ gst_mulawdec_chain (GstPad * pad, GstBuffer * buffer) /* ERRORS */ not_negotiated: { - GST_ERROR_OBJECT (mulawdec, "no format negotiated"); + GST_WARNING_OBJECT (mulawdec, "no input format set: not-negotiated"); gst_buffer_unref (buffer); return GST_FLOW_NOT_NEGOTIATED; } alloc_failed: { - GST_ERROR_OBJECT (mulawdec, "pad alloc failed"); + GST_DEBUG_OBJECT (mulawdec, "pad alloc failed, flow: %s", + gst_flow_get_name (ret)); gst_buffer_unref (buffer); return ret; } diff --git a/gst/law/mulaw-encode.c b/gst/law/mulaw-encode.c index f60e0b2e..022e96f1 100644 --- a/gst/law/mulaw-encode.c +++ b/gst/law/mulaw-encode.c @@ -75,8 +75,8 @@ mulawenc_getcaps (GstPad * pad) GstStructure *structure; const GValue *orate, *ochans; const GValue *rate, *chans; - GValue irate = { 0 }, ichans = { - 0}; + GValue irate = { 0 }; + GValue ichans = { 0 }; if (gst_caps_is_empty (othercaps) || gst_caps_is_any (othercaps)) goto done; @@ -84,22 +84,20 @@ mulawenc_getcaps (GstPad * pad) structure = gst_caps_get_structure (othercaps, 0); orate = gst_structure_get_value (structure, "rate"); ochans = gst_structure_get_value (structure, "channels"); - if (!orate || !ochans) - goto done; structure = gst_caps_get_structure (base_caps, 0); rate = gst_structure_get_value (structure, "rate"); chans = gst_structure_get_value (structure, "channels"); - if (!rate || !chans) - goto done; - gst_value_intersect (&irate, orate, rate); - gst_value_intersect (&ichans, ochans, chans); + if (orate) { + gst_value_intersect (&irate, orate, rate); + gst_structure_set_value (structure, "rate", &irate); + } - /* Set the samplerate/channels on the to-be-returned caps */ - structure = gst_caps_get_structure (base_caps, 0); - gst_structure_set_value (structure, "rate", &irate); - gst_structure_set_value (structure, "channels", &ichans); + if (ochans) { + gst_value_intersect (&ichans, ochans, chans); + gst_structure_set_value (structure, "channels", &ichans); + } done: gst_caps_unref (othercaps); diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 3c656c2c..50011cff 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -3864,7 +3864,7 @@ gst_matroska_demux_check_subtitle_buffer (GstElement * element, GST_BUFFER_SIZE (newbuf) = strlen (utf8); gst_buffer_copy_metadata (newbuf, *buf, GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_FLAGS); - gst_buffer_unref (buf); + gst_buffer_unref (*buf); *buf = newbuf; return GST_FLOW_OK; @@ -5135,9 +5135,6 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * guint rformat; guint subformat; - gst_util_dump_mem (data, size); - gst_util_dump_mem (data + 0x1a, size - 0x1a); - subformat = GST_READ_UINT32_BE (data + 0x1a); rformat = GST_READ_UINT32_BE (data + 0x1e); @@ -5503,7 +5500,6 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * guint extra_data_size; GST_ERROR ("real audio raversion:%d", raversion); - gst_util_dump_mem (data, size); if (raversion == 8) { /* COOK */ flavor = GST_READ_UINT16_BE (data + 22); diff --git a/gst/matroska/matroska-mux.c b/gst/matroska/matroska-mux.c index 746e3d19..1b958d69 100644 --- a/gst/matroska/matroska-mux.c +++ b/gst/matroska/matroska-mux.c @@ -650,7 +650,8 @@ gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps) gst_structure_get_int (structure, "height", &height); videocontext->pixel_width = width; videocontext->pixel_height = height; - if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)) { + if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d) + && fps_n > 0) { context->default_duration = gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n); GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT, diff --git a/gst/qtdemux/qtdemux.c b/gst/qtdemux/qtdemux.c index fe555a19..1cff4148 100644 --- a/gst/qtdemux/qtdemux.c +++ b/gst/qtdemux/qtdemux.c @@ -3109,11 +3109,11 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, index = QT_UINT32 ((guint8 *) stps->data + offset); if (index > 0 && index <= stream->n_samples) { samples[index - 1].keyframe = TRUE; - offset += 4; + offset += 4; + } } } } - } } else { /* no stss, all samples are keyframes */ stream->all_keyframe = TRUE; @@ -4184,6 +4184,7 @@ static const struct FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, { FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, { FOURCC__too, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, { + FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, { FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, { FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT, qtdemux_tag_add_num}, { diff --git a/gst/qtdemux/qtdemux_fourcc.h b/gst/qtdemux/qtdemux_fourcc.h index b4dabd73..cbf5b15f 100644 --- a/gst/qtdemux/qtdemux_fourcc.h +++ b/gst/qtdemux/qtdemux_fourcc.h @@ -90,6 +90,7 @@ G_BEGIN_DECLS #define FOURCC__day GST_MAKE_FOURCC(0xa9,'d','a','y') #define FOURCC__req GST_MAKE_FOURCC(0xa9,'r','e','q') #define FOURCC__enc GST_MAKE_FOURCC(0xa9,'e','n','c') +#define FOURCC__inf GST_MAKE_FOURCC(0xa9,'i','n','f') #define FOURCC_cprt GST_MAKE_FOURCC('c','p','r','t') #define FOURCC_gnre GST_MAKE_FOURCC('g','n','r','e') #define FOURCC_disc GST_MAKE_FOURCC('d','i','s','c') diff --git a/gst/replaygain/gstrgvolume.c b/gst/replaygain/gstrgvolume.c index 41fe441d..cf5a914a 100644 --- a/gst/replaygain/gstrgvolume.c +++ b/gst/replaygain/gstrgvolume.c @@ -563,6 +563,20 @@ gst_rg_volume_tag_event (GstRgVolume * self, GstEvent * event) has_album_peak = FALSE; } + /* Clamp peaks >1.0. Float based decoders can produce spurious samples >1.0, + * cutting these files back to 1.0 should not cause any audible distortion. + * This is most often seen with Vorbis files. */ + if (has_track_peak && self->track_peak > 1.) { + GST_DEBUG_OBJECT (self, + "clamping track peak %" PEAK_FORMAT " to 1.0", self->track_peak); + self->track_peak = 1.0; + } + if (has_album_peak && self->album_peak > 1.) { + GST_DEBUG_OBJECT (self, + "clamping album peak %" PEAK_FORMAT " to 1.0", self->album_peak); + self->album_peak = 1.0; + } + self->has_track_gain |= has_track_gain; self->has_track_peak |= has_track_peak; self->has_album_gain |= has_album_gain; diff --git a/gst/rtp/gstrtph264pay.c b/gst/rtp/gstrtph264pay.c index f0cd9ee8..a788316b 100644 --- a/gst/rtp/gstrtph264pay.c +++ b/gst/rtp/gstrtph264pay.c @@ -449,15 +449,17 @@ is_nal_equal (const guint8 * nal1, const guint8 * nal2, guint len) if (!remainder) { return TRUE; } else if (1 == remainder) { - return (nal1[--len] == nal2[len]); + --len; + return (nal1[len] == nal2[len]); } else { /* 2 or 3 */ if (remainder & 1) { /* -1 if 3 bytes left */ - if (nal1[--len] != nal2[len]) + --len; + if (nal1[len] != nal2[len]) return FALSE; } /* last 2 bytes */ - return ((nal1[--len] == nal2[len]) /* -1 */ - &&(nal1[--len] == nal2[len])); /* -2 */ + return ((nal1[len - 1] == nal2[len - 1]) /* -1 */ + &&(nal1[len - 2] == nal2[len - 2])); /* -2 */ } } @@ -467,89 +469,71 @@ gst_rtp_h264_pay_decode_nal (GstRtpH264Pay * payloader, { guint8 *sps = NULL, *pps = NULL; guint sps_len = 0, pps_len = 0; + guint8 header, type; + guint len; /* default is no update */ *updated = FALSE; - if (size <= 3) { - GST_WARNING ("Encoded buffer len %u <= 3", size); - } else { - GST_DEBUG ("NAL payload len=%u", size); + GST_DEBUG ("NAL payload len=%u", size); - /* loop through all NAL units and save the locations of any - * SPS / PPS for later processing. Only the last seen SPS - * or PPS will be considered */ - while (size > 5) { - guint8 header, type; - guint len; + len = size; + header = data[0]; + type = header & 0x1f; - len = next_start_code (data, size); - header = data[0]; - type = header & 0x1f; - - /* keep sps & pps separately so that we can update either one - * independently */ - if (SPS_TYPE_ID == type) { - /* encode the entire SPS NAL in base64 */ - GST_DEBUG ("Found SPS %x %x %x Len=%u", (header >> 7), - (header >> 5) & 3, type, len); - - sps = data; - sps_len = len; - } else if (PPS_TYPE_ID == type) { - /* encoder the entire PPS NAL in base64 */ - GST_DEBUG ("Found PPS %x %x %x Len = %u", - (header >> 7), (header >> 5) & 3, type, len); - - pps = data; - pps_len = len; - } else { - GST_DEBUG ("NAL: %x %x %x Len = %u", (header >> 7), - (header >> 5) & 3, type, len); - } + /* keep sps & pps separately so that we can update either one + * independently */ + if (SPS_TYPE_ID == type) { + /* encode the entire SPS NAL in base64 */ + GST_DEBUG ("Found SPS %x %x %x Len=%u", (header >> 7), + (header >> 5) & 3, type, len); - /* end of loop */ - if (len >= size - 4) { - break; - } + sps = data; + sps_len = len; + } else if (PPS_TYPE_ID == type) { + /* encoder the entire PPS NAL in base64 */ + GST_DEBUG ("Found PPS %x %x %x Len = %u", + (header >> 7), (header >> 5) & 3, type, len); - /* next NAL start */ - data += len + 4; - size -= len + 4; - } + pps = data; + pps_len = len; + } else { + GST_DEBUG ("NAL: %x %x %x Len = %u", (header >> 7), + (header >> 5) & 3, type, len); + } - /* If we encountered an SPS and/or a PPS, check if it's the - * same as the one we have. If not, update our version and - * set *updated to TRUE - */ - if (sps_len > 0) { - if ((payloader->sps_len != sps_len) - || !is_nal_equal (payloader->sps, sps, sps_len)) { - payloader->profile = (sps[1] << 16) + (sps[2] << 8) + sps[3]; - GST_DEBUG ("Profile level IDC = %06x", payloader->profile); + /* If we encountered an SPS and/or a PPS, check if it's the + * same as the one we have. If not, update our version and + * set *updated to TRUE + */ + if (sps_len > 0) { + if ((payloader->sps_len != sps_len) + || !is_nal_equal (payloader->sps, sps, sps_len)) { + payloader->profile = (sps[1] << 16) + (sps[2] << 8) + sps[3]; - if (payloader->sps_len) - g_free (payloader->sps); + GST_DEBUG ("Profile level IDC = %06x", payloader->profile); - payloader->sps = sps_len ? g_new (guint8, sps_len) : NULL; - memcpy (payloader->sps, sps, sps_len); - payloader->sps_len = sps_len; - *updated = TRUE; - } + if (payloader->sps_len) + g_free (payloader->sps); + + payloader->sps = sps_len ? g_new (guint8, sps_len) : NULL; + memcpy (payloader->sps, sps, sps_len); + payloader->sps_len = sps_len; + *updated = TRUE; } + } - if (pps_len > 0) { - if ((payloader->pps_len != pps_len) - || !is_nal_equal (payloader->pps, pps, pps_len)) { - if (payloader->pps_len) - g_free (payloader->pps); + if (pps_len > 0) { + if ((payloader->pps_len != pps_len) + || !is_nal_equal (payloader->pps, pps, pps_len)) { + if (payloader->pps_len) + g_free (payloader->pps); - payloader->pps = pps_len ? g_new (guint8, pps_len) : NULL; - memcpy (payloader->pps, pps, pps_len); - payloader->pps_len = pps_len; - *updated = TRUE; - } + payloader->pps = pps_len ? g_new (guint8, pps_len) : NULL; + memcpy (payloader->pps, pps, pps_len); + payloader->pps_len = pps_len; + *updated = TRUE; } } } diff --git a/gst/rtp/gstrtpvrawdepay.c b/gst/rtp/gstrtpvrawdepay.c index 35e68a3e..3f599f5d 100644 --- a/gst/rtp/gstrtpvrawdepay.c +++ b/gst/rtp/gstrtpvrawdepay.c @@ -139,6 +139,11 @@ gst_rtp_vraw_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) goto no_height; height = atoi (str); + /* optional interlace value but we don't handle interlaced + * formats yet */ + if ((str = gst_structure_get_string (structure, "interlace"))) + goto interlaced; + if (!(str = gst_structure_get_string (structure, "sampling"))) goto no_sampling; @@ -248,6 +253,11 @@ no_height: GST_ERROR_OBJECT (depayload, "no height specified"); return FALSE; } +interlaced: + { + GST_ERROR_OBJECT (depayload, "interlaced formats not supported yet"); + return FALSE; + } no_sampling: { GST_ERROR_OBJECT (depayload, "no sampling specified"); diff --git a/gst/rtp/gstrtpvrawpay.c b/gst/rtp/gstrtpvrawpay.c index 19c14c2b..0f0ef7e8 100644 --- a/gst/rtp/gstrtpvrawpay.c +++ b/gst/rtp/gstrtpvrawpay.c @@ -211,6 +211,7 @@ gst_rtp_vraw_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) GstVideoFormat sampling; const gchar *depthstr, *samplingstr, *colorimetrystr; gchar *wstr, *hstr; + gboolean interlaced; rtpvrawpay = GST_RTP_VRAW_PAY (payload); @@ -229,6 +230,13 @@ gst_rtp_vraw_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) if (!res) goto missing_dimension; + /* fail on interlaced video for now */ + if (!gst_structure_get_boolean (s, "interlaced", &interlaced)) + interlaced = FALSE; + + if (interlaced) + goto interlaced; + yp = up = vp = 0; xinc = yinc = 1; @@ -358,6 +366,11 @@ unknown_fourcc: GST_ERROR_OBJECT (payload, "invalid or missing fourcc"); return FALSE; } +interlaced: + { + GST_ERROR_OBJECT (payload, "interlaced video not supported yet"); + return FALSE; + } missing_dimension: { GST_ERROR_OBJECT (payload, "missing width or height property"); @@ -409,7 +422,7 @@ gst_rtp_vraw_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer) GstBuffer *out; guint8 *outdata, *headers; gboolean next_line; - guint length, cont, pixels; + guint length, cont, pixels, fieldid; /* get the max allowed payload length size, we try to fill the complete MTU */ left = gst_rtp_buffer_calc_payload_len (mtu, 0, 0); @@ -422,6 +435,24 @@ gst_rtp_vraw_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer) GST_LOG_OBJECT (rtpvrawpay, "created buffer of size %u for MTU %u", left, mtu); + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Extended Sequence Number | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |F| Line No |C| Offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Length |F| Line No | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |C| Offset | . + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ . + * . . + * . Two (partial) lines of video data . + * . . + * +---------------------------------------------------------------+ + */ + /* need 2 bytes for the extended sequence number */ *outdata++ = 0; *outdata++ = 0; @@ -456,8 +487,12 @@ gst_rtp_vraw_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer) /* write length */ *outdata++ = (length >> 8) & 0xff; *outdata++ = length & 0xff; + + /* always 0 for now */ + fieldid = 0x00; + /* write line no */ - *outdata++ = (line >> 8) & 0x7f; + *outdata++ = ((line >> 8) & 0x7f) | fieldid; *outdata++ = line & 0xff; if (next_line) { diff --git a/gst/rtsp/gstrtspext.c b/gst/rtsp/gstrtspext.c index 0ad81b56..a3216794 100644 --- a/gst/rtsp/gstrtspext.c +++ b/gst/rtsp/gstrtspext.c @@ -247,3 +247,20 @@ gst_rtsp_ext_list_connect (GstRTSPExtensionList * ext, g_signal_connect (elem, detailed_signal, c_handler, data); } } + +GstRTSPResult +gst_rtsp_ext_list_receive_request (GstRTSPExtensionList * ext, + GstRTSPMessage * req) +{ + GList *walk; + GstRTSPResult res = GST_RTSP_ENOTIMPL; + + for (walk = ext->extensions; walk; walk = g_list_next (walk)) { + GstRTSPExtension *elem = (GstRTSPExtension *) walk->data; + + res = gst_rtsp_extension_receive_request (elem, req); + if (res != GST_RTSP_ENOTIMPL) + break; + } + return res; +} diff --git a/gst/rtsp/gstrtspext.h b/gst/rtsp/gstrtspext.h index fa7f6892..f30b302f 100644 --- a/gst/rtsp/gstrtspext.h +++ b/gst/rtsp/gstrtspext.h @@ -76,6 +76,7 @@ GstRTSPResult gst_rtsp_ext_list_stream_select (GstRTSPExtensionList *ext, Gs void gst_rtsp_ext_list_connect (GstRTSPExtensionList *ext, const gchar *detailed_signal, GCallback c_handler, gpointer data); +GstRTSPResult gst_rtsp_ext_list_receive_request (GstRTSPExtensionList *ext, GstRTSPMessage *req); G_END_DECLS diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index 02970c42..7c5ef8a6 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -146,6 +146,7 @@ enum #define DEFAULT_LATENCY_MS 3000 #define DEFAULT_CONNECTION_SPEED 0 #define DEFAULT_NAT_METHOD GST_RTSP_NAT_DUMMY +#define DEFAULT_DO_RTCP TRUE enum { @@ -159,6 +160,7 @@ enum PROP_LATENCY, PROP_CONNECTION_SPEED, PROP_NAT_METHOD, + PROP_DO_RTCP, PROP_LAST }; @@ -335,6 +337,19 @@ gst_rtspsrc_class_init (GstRTSPSrcClass * klass) GST_TYPE_RTSP_NAT_METHOD, DEFAULT_NAT_METHOD, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + /** + * GstRTSPSrc::do-rtcp + * + * Enable RTCP support. Some old server don't like RTCP and then this property + * needs to be set to FALSE. + * + * Since: 0.10.15 + */ + g_object_class_install_property (gobject_class, PROP_DO_RTCP, + g_param_spec_boolean ("do-rtcp", "Do RTCP", + "Don't send RTCP packets", + DEFAULT_DO_RTCP, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + gstelement_class->change_state = gst_rtspsrc_change_state; gstbin_class->handle_message = gst_rtspsrc_handle_message; @@ -454,6 +469,9 @@ gst_rtspsrc_set_property (GObject * object, guint prop_id, const GValue * value, case PROP_NAT_METHOD: rtspsrc->nat_method = g_value_get_enum (value); break; + case PROP_DO_RTCP: + rtspsrc->do_rtcp = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -502,6 +520,9 @@ gst_rtspsrc_get_property (GObject * object, guint prop_id, GValue * value, case PROP_NAT_METHOD: g_value_set_enum (value, rtspsrc->nat_method); break; + case PROP_DO_RTCP: + g_value_set_boolean (value, rtspsrc->do_rtcp); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1942,8 +1963,8 @@ gst_rtspsrc_stream_configure_tcp (GstRTSPSrc * src, GstRTSPStream * stream, } gst_object_unref (template); } - /* setup RTCP transport back to the server */ - if (src->session) { + /* setup RTCP transport back to the server if we have to. */ + if (src->session && src->do_rtcp) { GstPad *pad; template = gst_static_pad_template_get (&anysinktemplate); @@ -2162,7 +2183,7 @@ gst_rtspsrc_stream_configure_udp_sinks (GstRTSPSrc * src, } /* it's possible that the server does not want us to send RTCP in which case * the port is -1 */ - if (rtcp_port != -1 && src->session != NULL) { + if (rtcp_port != -1 && src->session != NULL && src->do_rtcp) { GST_DEBUG_OBJECT (src, "configure RTCP UDP sink for %s:%d", destination, rtcp_port); @@ -2536,20 +2557,25 @@ gst_rtspsrc_handle_request (GstRTSPSrc * src, GstRTSPMessage * request) if (src->debug) gst_rtsp_message_dump (request); - res = - gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, "OK", - request); - if (res < 0) - goto send_error; + res = gst_rtsp_ext_list_receive_request (src->extensions, request); + + if (res == GST_RTSP_ENOTIMPL) { + /* default implementation, send OK */ + res = + gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, "OK", + request); + if (res < 0) + goto send_error; - GST_DEBUG_OBJECT (src, "replying with OK"); + GST_DEBUG_OBJECT (src, "replying with OK"); - if (src->debug) - gst_rtsp_message_dump (&response); + if (src->debug) + gst_rtsp_message_dump (&response); - res = gst_rtspsrc_connection_send (src, &response, NULL); - if (res < 0) - goto send_error; + res = gst_rtspsrc_connection_send (src, &response, NULL); + if (res < 0) + goto send_error; + } return GST_RTSP_OK; @@ -3785,7 +3811,8 @@ failed: } static GstRTSPResult -gst_rtspsrc_prepare_transports (GstRTSPStream * stream, gchar ** transports) +gst_rtspsrc_prepare_transports (GstRTSPStream * stream, gchar ** transports, + gint orig_rtpport, gint orig_rtcpport) { GstRTSPSrc *src; gint nr_udp, nr_int; @@ -3814,8 +3841,13 @@ gst_rtspsrc_prepare_transports (GstRTSPStream * stream, gchar ** transports) goto done; if (nr_udp > 0) { - if (!gst_rtspsrc_alloc_udp_ports (stream, &rtpport, &rtcpport)) - goto failed; + if (!orig_rtpport || !orig_rtcpport) { + if (!gst_rtspsrc_alloc_udp_ports (stream, &rtpport, &rtcpport)) + goto failed; + } else { + rtpport = orig_rtpport; + rtcpport = orig_rtcpport; + } } str = g_string_new (""); @@ -3875,6 +3907,7 @@ gst_rtspsrc_setup_streams (GstRTSPSrc * src) GstRTSPStream *stream = NULL; GstRTSPLowerTrans protocols; GstRTSPStatusCode code; + gint rtpport, rtcpport; /* we initially allow all configured lower transports. based on the URL * transports and the replies from the server we narrow them down. */ @@ -3887,9 +3920,11 @@ gst_rtspsrc_setup_streams (GstRTSPSrc * src) src->free_channel = 0; src->interleaved = FALSE; src->need_activate = FALSE; + rtpport = rtcpport = 0; for (walk = src->streams; walk; walk = g_list_next (walk)) { gchar *transports; + gint retry = 0; stream = (GstRTSPStream *) walk->data; @@ -3930,6 +3965,7 @@ gst_rtspsrc_setup_streams (GstRTSPSrc * src) GST_DEBUG_OBJECT (src, "doing setup of stream %p with %s", stream, stream->setup_url); + retry: /* create a string with all the transports */ res = gst_rtspsrc_create_transports_string (src, protocols, &transports); if (res < 0) @@ -3939,7 +3975,8 @@ gst_rtspsrc_setup_streams (GstRTSPSrc * src) /* replace placeholders with real values, this function will optionally * allocate UDP ports and other info needed to execute the setup request */ - res = gst_rtspsrc_prepare_transports (stream, &transports); + res = gst_rtspsrc_prepare_transports (stream, &transports, + retry > 0 ? rtpport : 0, retry > 0 ? rtcpport : 0); if (res < 0) goto setup_transport_failed; @@ -3966,8 +4003,17 @@ gst_rtspsrc_setup_streams (GstRTSPSrc * src) case GST_RTSP_STS_UNSUPPORTED_TRANSPORT: gst_rtsp_message_unset (&request); gst_rtsp_message_unset (&response); - /* cleanup of leftover transport and move to the next stream */ + /* cleanup of leftover transport */ gst_rtspsrc_stream_free_udp (stream); + /* MS WMServer RTSP MUST use same UDP pair in all SETUP requests; + * we might be in this case */ + if (stream->container && rtpport && rtcpport && !retry) { + GST_DEBUG_OBJECT (src, "retrying with original port pair %u-%u", + rtpport, rtcpport); + retry++; + goto retry; + } + /* give up on this stream and move to the next stream */ continue; default: /* cleanup of leftover transport and move to the next stream */ @@ -4024,13 +4070,17 @@ gst_rtspsrc_setup_streams (GstRTSPSrc * src) break; } - if (!stream->container || !src->interleaved) { + if (!stream->container || (!src->interleaved && !retry)) { /* now configure the stream with the selected transport */ if (!gst_rtspsrc_stream_configure_transport (stream, &transport)) { GST_DEBUG_OBJECT (src, "could not configure stream %p transport, skipping stream", stream); goto next; + } else if (stream->udpsrc[0] && stream->udpsrc[1]) { + /* retain the first allocated UDP port pair */ + g_object_get (G_OBJECT (stream->udpsrc[0]), "port", &rtpport, NULL); + g_object_get (G_OBJECT (stream->udpsrc[1]), "port", &rtcpport, NULL); } } /* we need to activate at least one streams when we detect activity */ diff --git a/gst/rtsp/gstrtspsrc.h b/gst/rtsp/gstrtspsrc.h index 95dd9869..40a368c6 100644 --- a/gst/rtsp/gstrtspsrc.h +++ b/gst/rtsp/gstrtspsrc.h @@ -186,6 +186,7 @@ struct _GstRTSPSrc { guint latency; guint connection_speed; GstRTSPNatMethod nat_method; + gboolean do_rtcp; /* state */ GstRTSPState state; diff --git a/gst/udp/gstudpnetutils.c b/gst/udp/gstudpnetutils.c index a1588caa..5fa7593a 100644 --- a/gst/udp/gstudpnetutils.c +++ b/gst/udp/gstudpnetutils.c @@ -115,12 +115,17 @@ beach: int gst_udp_set_loop_ttl (int sockfd, gboolean loop, int ttl) { + socklen_t socklen; + struct sockaddr_storage addr; int ret = -1; - -#if 0 int l = (loop == FALSE) ? 0 : 1; - switch (addr->ss_family) { + socklen = sizeof (addr); + if ((ret = getsockname (sockfd, (struct sockaddr *) &addr, &socklen)) < 0) { + return ret; + } + + switch (addr.ss_family) { case AF_INET: { if ((ret = @@ -149,9 +154,12 @@ gst_udp_set_loop_ttl (int sockfd, gboolean loop, int ttl) break; } default: +#ifdef G_OS_WIN32 + WSASetLastError (WSAEAFNOSUPPORT); +#else errno = EAFNOSUPPORT; - } #endif + } return ret; } diff --git a/gst/udp/gstudpsrc.c b/gst/udp/gstudpsrc.c index bcdbc359..8d92241e 100644 --- a/gst/udp/gstudpsrc.c +++ b/gst/udp/gstudpsrc.c @@ -357,13 +357,50 @@ gst_udpsrc_getcaps (GstBaseSrc * src) return gst_caps_new_any (); } +/* read a message from the error queue */ +static void +clear_error (GstUDPSrc * udpsrc) +{ +#if defined (MSG_ERRQUEUE) + struct msghdr cmsg; + char cbuf[128]; + char msgbuf[CMSG_SPACE (128)]; + struct iovec iov; + + /* Flush ERRORS from fd so next poll will not return at once */ + /* No need for address : We look for local error */ + cmsg.msg_name = NULL; + cmsg.msg_namelen = 0; + + /* IOV */ + memset (&cbuf, 0, sizeof (cbuf)); + iov.iov_base = cbuf; + iov.iov_len = sizeof (cbuf); + cmsg.msg_iov = &iov; + cmsg.msg_iovlen = 1; + + /* msg_control */ + memset (&msgbuf, 0, sizeof (msgbuf)); + cmsg.msg_control = &msgbuf; + cmsg.msg_controllen = sizeof (msgbuf); + + recvmsg (udpsrc->sock.fd, &cmsg, MSG_ERRQUEUE); +#endif +} + static GstFlowReturn gst_udpsrc_create (GstPushSrc * psrc, GstBuffer ** buf) { GstUDPSrc *udpsrc; GstNetBuffer *outbuf; - struct sockaddr_storage tmpaddr; - socklen_t len; + union gst_sockaddr + { + struct sockaddr sa; + struct sockaddr_in sa_in; + struct sockaddr_in6 sa_in6; + struct sockaddr_storage sa_stor; + } sa; + socklen_t slen; guint8 *pktdata; gint pktsize; #ifdef G_OS_UNIX @@ -434,8 +471,10 @@ retry: * woken up by activity on the socket but it was not a read. We know someone * will also do something with the socket so that we don't go into an infinite * loop in the select(). */ - if (G_UNLIKELY (!readsize)) + if (G_UNLIKELY (!readsize)) { + clear_error (udpsrc); goto retry; + } no_select: GST_LOG_OBJECT (udpsrc, "ioctl says %d bytes available", (int) readsize); @@ -444,13 +483,13 @@ no_select: pktsize = readsize; while (TRUE) { - len = sizeof (struct sockaddr); + slen = sizeof (sa); #ifdef G_OS_WIN32 - ret = recvfrom (udpsrc->sock.fd, (char *) pktdata, pktsize, + ret = recvfrom (udpsrc->sock.fd, (char *) pktdata, pktsize, 0, &sa.sa, + &slen); #else - ret = recvfrom (udpsrc->sock.fd, pktdata, pktsize, + ret = recvfrom (udpsrc->sock.fd, pktdata, pktsize, 0, &sa.sa, &slen); #endif - 0, (struct sockaddr *) &tmpaddr, &len); if (G_UNLIKELY (ret < 0)) { #ifdef G_OS_WIN32 /* WSAECONNRESET for a UDP socket means that a packet sent with udpsink @@ -486,22 +525,19 @@ no_select: GST_BUFFER_DATA (outbuf) = pktdata; GST_BUFFER_SIZE (outbuf) = ret; - switch (tmpaddr.ss_family) { + switch (sa.sa.sa_family) { case AF_INET: { - gst_netaddress_set_ip4_address (&outbuf->from, - ((struct sockaddr_in *) &tmpaddr)->sin_addr.s_addr, - ((struct sockaddr_in *) &tmpaddr)->sin_port); + gst_netaddress_set_ip4_address (&outbuf->from, sa.sa_in.sin_addr.s_addr, + sa.sa_in.sin_port); } break; case AF_INET6: { guint8 ip6[16]; - memcpy (ip6, &((struct sockaddr_in6 *) &tmpaddr)->sin6_addr, - sizeof (ip6)); - gst_netaddress_set_ip6_address (&outbuf->from, ip6, - ((struct sockaddr_in *) &tmpaddr)->sin_port); + memcpy (ip6, &sa.sa_in6.sin6_addr, sizeof (ip6)); + gst_netaddress_set_ip6_address (&outbuf->from, ip6, sa.sa_in6.sin6_port); } break; default: @@ -730,6 +766,7 @@ static gboolean gst_udpsrc_start (GstBaseSrc * bsrc) { guint bc_val; + guint err_val; gint reuse; int port; GstUDPSrc *src; @@ -821,6 +858,17 @@ gst_udpsrc_start (GstBaseSrc * bsrc) g_strerror (errno), errno)); } + /* Accept ERRQUEUE to get and flush icmp errors */ + err_val = 1; +#if defined (IP_RECVERR) + if ((ret = setsockopt (src->sock.fd, IPPROTO_IP, IP_RECVERR, &err_val, + sizeof (err_val))) < 0) { + GST_ELEMENT_WARNING (src, RESOURCE, SETTINGS, (NULL), + ("could not configure socket for IP_RECVERR %d: %s (%d)", ret, + g_strerror (errno), errno)); + } +#endif + if (src->auto_multicast && gst_udp_is_multicast (&src->myaddr)) { GST_DEBUG_OBJECT (src, "joining multicast group %s", src->multi_group); ret = gst_udp_join_group (src->sock.fd, &src->myaddr); diff --git a/gst/wavparse/gstwavparse.c b/gst/wavparse/gstwavparse.c index 266c430f..c8e7c756 100644 --- a/gst/wavparse/gstwavparse.c +++ b/gst/wavparse/gstwavparse.c @@ -77,6 +77,7 @@ static gboolean gst_wavparse_pad_convert (GstPad * pad, gint64 src_value, GstFormat * dest_format, gint64 * dest_value); static GstFlowReturn gst_wavparse_chain (GstPad * pad, GstBuffer * buf); +static gboolean gst_wavparse_sink_event (GstPad * pad, GstEvent * event); static void gst_wavparse_loop (GstPad * pad); static gboolean gst_wavparse_srcpad_event (GstPad * pad, GstEvent * event); @@ -193,6 +194,8 @@ gst_wavparse_init (GstWavParse * wavparse, GstWavParseClass * g_class) GST_DEBUG_FUNCPTR (gst_wavparse_sink_activate_pull)); gst_pad_set_chain_function (wavparse->sinkpad, GST_DEBUG_FUNCPTR (gst_wavparse_chain)); + gst_pad_set_event_function (wavparse->sinkpad, + GST_DEBUG_FUNCPTR (gst_wavparse_sink_event)); gst_element_add_pad (GST_ELEMENT_CAST (wavparse), wavparse->sinkpad); /* src, will be created later */ @@ -725,8 +728,35 @@ gst_wavparse_stream_init (GstWavParse * wav) return GST_FLOW_OK; } -/* This function is used to perform seeks on the element in - * pull mode. +static gboolean +gst_wavparse_time_to_bytepos (GstWavParse * wav, gint64 ts, gint64 * bytepos) +{ + /* -1 always maps to -1 */ + if (ts == -1) { + *bytepos = -1; + return TRUE; + } + + /* 0 always maps to 0 */ + if (ts == 0) { + *bytepos = 0; + return TRUE; + } + + if (wav->bps > 0) { + *bytepos = uint64_ceiling_scale (ts, (guint64) wav->bps, GST_SECOND); + return TRUE; + } else if (wav->fact) { + guint64 bps = + gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact); + *bytepos = uint64_ceiling_scale (ts, bps, GST_SECOND); + return TRUE; + } + + return FALSE; +} + +/* This function is used to perform seeks on the element. * * It also works when event is NULL, in which case it will just * start from the last configured segment. This technique is @@ -783,6 +813,48 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event) stop_type = GST_SEEK_TYPE_SET; } + /* in push mode, we must delegate to upstream */ + if (wav->streaming) { + gboolean res = FALSE; + + /* if streaming not yet started; only prepare initial newsegment */ + if (!event || wav->state != GST_WAVPARSE_DATA) { + if (wav->start_segment) + gst_event_unref (wav->start_segment); + wav->start_segment = + gst_event_new_new_segment (FALSE, wav->segment.rate, + wav->segment.format, wav->segment.last_stop, wav->segment.duration, + wav->segment.last_stop); + res = TRUE; + } else { + /* convert seek positions to byte positions in data sections */ + if (format == GST_FORMAT_TIME) { + /* should not fail */ + if (!gst_wavparse_time_to_bytepos (wav, cur, &cur)) + goto no_position; + if (!gst_wavparse_time_to_bytepos (wav, stop, &stop)) + goto no_position; + } + /* mind sample boundary and header */ + if (cur >= 0) { + cur -= (cur % wav->bytes_per_sample); + cur += wav->datastart; + } + if (stop >= 0) { + stop -= (stop % wav->bytes_per_sample); + stop += wav->datastart; + } + GST_DEBUG_OBJECT (wav, "Pushing BYTE seek rate %g, " + "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, cur, + stop); + /* BYTE seek event */ + event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, cur, + stop_type, stop); + res = gst_pad_push_event (wav->sinkpad, event); + } + return res; + } + /* get flush flag */ flush = flags & GST_SEEK_FLAG_FLUSH; @@ -832,16 +904,8 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event) /* bring offset to bytes, if the bps is 0, we have the segment in BYTES and * we can just copy the last_stop. If not, we use the bps to convert TIME to * bytes. */ - if (wav->bps > 0) - wav->offset = - uint64_ceiling_scale (seeksegment.last_stop, (guint64) wav->bps, - GST_SECOND); - else if (wav->fact) { - guint64 bps = - gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact); - wav->offset = - uint64_ceiling_scale (seeksegment.last_stop, bps, GST_SECOND); - } else + if (!gst_wavparse_time_to_bytepos (wav, seeksegment.last_stop, + (gint64 *) & wav->offset)) wav->offset = seeksegment.last_stop; GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset); wav->offset -= (wav->offset % wav->bytes_per_sample); @@ -854,14 +918,7 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event) } if (stop_type != GST_SEEK_TYPE_NONE) { - if (wav->bps > 0) - wav->end_offset = - uint64_ceiling_scale (stop, (guint64) wav->bps, GST_SECOND); - else if (wav->fact) { - guint64 bps = - gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact); - wav->end_offset = uint64_ceiling_scale (stop, bps, GST_SECOND); - } else + if (!gst_wavparse_time_to_bytepos (wav, stop, (gint64 *) & wav->end_offset)) wav->end_offset = stop; GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset); wav->end_offset -= (wav->end_offset % wav->bytes_per_sample); @@ -962,6 +1019,12 @@ no_format: GST_DEBUG_OBJECT (wav, "unsupported format given, seek aborted."); return FALSE; } +no_position: + { + GST_DEBUG_OBJECT (wav, + "Could not determine byte position for desired time"); + return FALSE; + } } /* @@ -1678,6 +1741,32 @@ iterate_adapter: if (wav->streaming) { guint avail = gst_adapter_available (wav->adapter); + guint extra; + + /* flush some bytes if evil upstream sends segment that starts + * before data or does is not send sample aligned segment */ + if (G_LIKELY (wav->offset >= wav->datastart)) { + extra = (wav->offset - wav->datastart) % wav->bytes_per_sample; + } else { + extra = wav->datastart - wav->offset; + } + + if (G_UNLIKELY (extra)) { + extra = wav->bytes_per_sample - extra; + if (extra <= avail) { + GST_DEBUG_OBJECT (wav, "flushing %d bytes to sample boundary", extra); + gst_adapter_flush (wav->adapter, extra); + wav->offset += extra; + wav->dataleft -= extra; + goto iterate_adapter; + } else { + GST_DEBUG_OBJECT (wav, "flushing %d bytes", avail); + gst_adapter_clear (wav->adapter); + wav->offset += avail; + wav->dataleft -= avail; + return GST_FLOW_OK; + } + } if (avail < desired) { GST_LOG_OBJECT (wav, "Got only %d bytes of data from the sinkpad", avail); @@ -1927,6 +2016,8 @@ gst_wavparse_chain (GstPad * pad, GstBuffer * buf) /* fall-through */ case GST_WAVPARSE_DATA: + if (buf && GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) + wav->discont = TRUE; if ((ret = gst_wavparse_stream_data (wav)) != GST_FLOW_OK) goto done; break; @@ -1937,6 +2028,132 @@ done: return ret; } +static GstFlowReturn +gst_wavparse_flush_data (GstWavParse * wav) +{ + GstFlowReturn ret = GST_FLOW_OK; + guint av; + + if ((av = gst_adapter_available (wav->adapter)) > 0) { + wav->dataleft = av; + wav->end_offset = wav->offset + av; + ret = gst_wavparse_stream_data (wav); + } + + return ret; +} + +static gboolean +gst_wavparse_sink_event (GstPad * pad, GstEvent * event) +{ + GstWavParse *wav = GST_WAVPARSE (GST_PAD_PARENT (pad)); + gboolean ret = TRUE; + + GST_LOG_OBJECT (wav, "handling %s event", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT: + { + GstFormat format; + gdouble rate, arate; + gint64 start, stop, time, offset = 0, end_offset = -1; + gboolean update; + GstSegment segment; + + /* some debug output */ + gst_segment_init (&segment, GST_FORMAT_UNDEFINED); + gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, + &start, &stop, &time); + gst_segment_set_newsegment_full (&segment, update, rate, arate, format, + start, stop, time); + GST_DEBUG_OBJECT (wav, + "received format %d newsegment %" GST_SEGMENT_FORMAT, format, + &segment); + + if (wav->state != GST_WAVPARSE_DATA) { + GST_DEBUG_OBJECT (wav, "still starting, eating event"); + goto exit; + } + + /* now we are either committed to TIME or BYTE format, + * and we only expect a BYTE segment, e.g. following a seek */ + if (format == GST_FORMAT_BYTES) { + if (start > 0) { + offset = start; + start -= wav->datastart; + start = MAX (start, 0); + } + if (stop > 0) { + end_offset = stop; + stop -= wav->datastart; + stop = MAX (stop, 0); + } + if (wav->segment.format == GST_FORMAT_TIME) { + guint64 bps = wav->bps; + + /* operating in format TIME, so we can convert */ + if (!bps && wav->fact) + bps = + gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact); + if (bps) { + if (start >= 0) + start = + uint64_ceiling_scale (start, GST_SECOND, (guint64) wav->bps); + if (stop >= 0) + stop = + uint64_ceiling_scale (stop, GST_SECOND, (guint64) wav->bps); + } + } + } else { + GST_DEBUG_OBJECT (wav, "unsupported segment format, ignoring"); + goto exit; + } + + /* accept upstream's notion of segment and distribute along */ + gst_segment_set_newsegment_full (&wav->segment, update, rate, arate, + wav->segment.format, start, stop, start); + /* also store the newsegment event for the streaming thread */ + if (wav->start_segment) + gst_event_unref (wav->start_segment); + wav->start_segment = + gst_event_new_new_segment_full (update, rate, arate, + wav->segment.format, start, stop, start); + GST_DEBUG_OBJECT (wav, "Pushing newseg update %d, rate %g, " + "applied rate %g, format %d, start %" G_GINT64_FORMAT ", " + "stop %" G_GINT64_FORMAT, update, rate, arate, wav->segment.format, + start, stop); + + /* stream leftover data in current segment */ + gst_wavparse_flush_data (wav); + /* and set up streaming thread for next one */ + wav->offset = offset; + wav->end_offset = end_offset; + if (wav->end_offset > 0) { + wav->dataleft = wav->end_offset - wav->offset; + } else { + /* infinity; upstream will EOS when done */ + wav->dataleft = G_MAXUINT64; + } + exit: + gst_event_unref (event); + break; + } + case GST_EVENT_EOS: + /* stream leftover data in current segment */ + gst_wavparse_flush_data (wav); + /* fall-through */ + case GST_EVENT_FLUSH_STOP: + gst_adapter_clear (wav->adapter); + wav->discont = TRUE; + /* fall-through */ + default: + ret = gst_pad_event_default (wav->sinkpad, event); + break; + } + + return ret; +} + #if 0 /* convert and query stuff */ static const GstFormat * @@ -2089,6 +2306,8 @@ gst_wavparse_pad_query (GstPad * pad, GstQuery * query) return FALSE; } + GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query)); + switch (GST_QUERY_TYPE (query)) { case GST_QUERY_POSITION: { @@ -2152,20 +2371,31 @@ gst_wavparse_pad_query (GstPad * pad, GstQuery * query) } case GST_QUERY_SEEKING:{ GstFormat fmt; + gboolean seekable = FALSE; gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL); - if (fmt == GST_FORMAT_TIME) { - gboolean seekable = TRUE; + if (fmt == wav->segment.format) { + res = TRUE; + if (wav->streaming) { + GstQuery *q; - if ((wav->bps == 0) && !wav->fact) { - seekable = FALSE; - } else if (!gst_wavparse_calculate_duration (wav)) { - seekable = FALSE; + q = gst_query_new_seeking (GST_FORMAT_BYTES); + if ((res = gst_pad_peer_query (wav->sinkpad, q))) { + gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL); + GST_LOG_OBJECT (wav, "upstream BYTE seekable %d", seekable); + } + gst_query_unref (q); + } else { + GST_LOG_OBJECT (wav, "looping => seekable"); + seekable = TRUE; + res = TRUE; } - gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, - 0, wav->duration); + } else if (fmt == GST_FORMAT_TIME) { res = TRUE; } + if (res) { + gst_query_set_seeking (query, fmt, seekable, 0, wav->segment.duration); + } break; } default: |