diff --git a/.gitignore b/.gitignore index 2ff4d5a7d5..91867b2857 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,5 @@ gst/deinterlace/tvtime.h tmp-orc.c *orc.h + +/tests/examples/jack/jack_client diff --git a/ChangeLog b/ChangeLog index 72ede1114b..941cfb4198 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,3301 @@ -=== release 0.10.26 === +=== release 0.10.27 === -2010-12-01 Tim-Philipp Müller +2011-01-21 Tim-Philipp Müller * configure.ac: - releasing 0.10.26, "Escapades" + releasing 0.10.27, "Some Kind of Temporal Blend" + +2011-01-20 14:10:55 +0000 Tim-Philipp Müller + + * gst/rtp/gstrtph264depay.c: + h264depay: don't leak codec data buffer in byte-stream=true mode + https://bugzilla.gnome.org/show_bug.cgi?id=640063 + +2011-01-20 13:41:33 +0000 Tim-Philipp Müller + + * gst/rtsp/gstrtspsrc.c: + rtspsrc: don't leak url string + https://bugzilla.gnome.org/show_bug.cgi?id=640064 + +2011-01-20 11:45:47 +0100 Edward Hervey + + * gst/qtdemux/qtdemux.c: + qtdemux: Gracefully handle mov files misusing the WAVE atoms + Check that the WAVEHEADER node is present instead of blindly using it. + If not present we won't be able to provide a more refined caps, but at + least we won't crash. + https://bugzilla.gnome.org/show_bug.cgi?id=640028 + +2011-01-20 00:07:33 +0000 Tim-Philipp Müller + + * sys/v4l2/gstv4l2sink.c: + v4l2sink: fix accidental breakage of navigation interface support + +2011-01-18 12:58:29 +0000 Tim-Philipp Müller + + * configure.ac: + * win32/common/config.h: + 0.10.26.4 pre-release + +2011-01-12 14:03:57 -0800 David Schleef + + * gst/deinterlace/gstdeinterlacemethod.c: + deinterlace: rewrite how neighboring scan lines are calculated + Old code was difficult to understand exactly how the neighboring + scan lines are calculated, and it appeared that some were off by + +2 or -2, depending on the field flag. Fixes #639321. + +2011-01-18 09:33:06 +0000 Tim-Philipp Müller + + * gst/avi/gstavisubtitle.c: + avisubtitle: set caps on srcpad to fix issue with discoverer + Set caps from the start so discoverer doesn't blow up on + seeing no negotiated caps between elements on preroll, + which might happen if no subtitle buffers have been + pushed yet at the time. See file from bug #603308. + +2011-01-17 20:09:16 +0530 Arun Raghavan + + * ext/pulse/pulsesink.c: + pulsesink: Uncork stream while flushing the ringbuffer + After starting the ringbuffer, we wait for enough data to arrive before + uncorking the stream. This will cause the pipeline to stall if we get an + EOS (or otherwise need to flush the stream) before sufficient data + becomes available. This patch makes sure that the stream is uncorked + while flushing to avoid this problem. + Fixes issue with a webkit unit test testing reverse playback of + an MP4 H.264/AAC file. + https://bugzilla.gnome.org/show_bug.cgi?id=639740 + +2011-01-14 14:51:51 +0100 Mark Nauwelaerts + + * gst/matroska/matroska-mux.c: + matroskamux: avoid creating caps from string when possible + Fixes #639516. + +2011-01-14 14:48:49 +0100 Mark Nauwelaerts + + * gst/avi/gstavimux.c: + avimux: set src pad caps when starting file + Fixes #639516. + +2011-01-12 20:38:59 +0000 Tim-Philipp Müller + + * sys/v4l2/gstv4l2bufferpool.c: + * sys/v4l2/gstv4l2object.c: + v4l2: define V4L2_FIELD_INTERLACED_{TB,BT} if not available in header + Older kernels don't have these, and there's no easy way to check for the + existance of enums that doesn't involve a configure check, so just define + these if the V4L2_CAP_VIDEO_OUTPUT_OVERLAY define is not there, which was + added in the same commit as the TB/BT enum. Fixes compilation on CentOS 5. + https://bugzilla.gnome.org/show_bug.cgi?id=639339 + +2011-01-11 23:18:59 +0000 Tim-Philipp Müller + + * configure.ac: + * win32/common/config.h: + 0.10.26.3 pre-release + +2011-01-11 22:42:42 +0000 Tim-Philipp Müller + + * docs/plugins/gst-plugins-good-plugins.args: + * docs/plugins/gst-plugins-good-plugins.hierarchy: + * docs/plugins/gst-plugins-good-plugins.interfaces: + * docs/plugins/gst-plugins-good-plugins.prerequisites: + * docs/plugins/inspect/plugin-1394.xml: + * docs/plugins/inspect/plugin-aasink.xml: + * docs/plugins/inspect/plugin-alaw.xml: + * docs/plugins/inspect/plugin-alpha.xml: + * docs/plugins/inspect/plugin-alphacolor.xml: + * docs/plugins/inspect/plugin-annodex.xml: + * docs/plugins/inspect/plugin-apetag.xml: + * docs/plugins/inspect/plugin-audiofx.xml: + * docs/plugins/inspect/plugin-auparse.xml: + * docs/plugins/inspect/plugin-autodetect.xml: + * docs/plugins/inspect/plugin-avi.xml: + * docs/plugins/inspect/plugin-cacasink.xml: + * docs/plugins/inspect/plugin-cairo.xml: + * docs/plugins/inspect/plugin-cutter.xml: + * docs/plugins/inspect/plugin-debug.xml: + * docs/plugins/inspect/plugin-deinterlace.xml: + * docs/plugins/inspect/plugin-dv.xml: + * docs/plugins/inspect/plugin-efence.xml: + * docs/plugins/inspect/plugin-effectv.xml: + * docs/plugins/inspect/plugin-equalizer.xml: + * docs/plugins/inspect/plugin-esdsink.xml: + * docs/plugins/inspect/plugin-flac.xml: + * docs/plugins/inspect/plugin-flv.xml: + * docs/plugins/inspect/plugin-flxdec.xml: + * docs/plugins/inspect/plugin-gconfelements.xml: + * docs/plugins/inspect/plugin-gdkpixbuf.xml: + * docs/plugins/inspect/plugin-goom.xml: + * docs/plugins/inspect/plugin-goom2k1.xml: + * docs/plugins/inspect/plugin-gstrtpmanager.xml: + * docs/plugins/inspect/plugin-halelements.xml: + * docs/plugins/inspect/plugin-icydemux.xml: + * docs/plugins/inspect/plugin-id3demux.xml: + * docs/plugins/inspect/plugin-imagefreeze.xml: + * docs/plugins/inspect/plugin-interleave.xml: + * docs/plugins/inspect/plugin-jack.xml: + * docs/plugins/inspect/plugin-jpeg.xml: + * docs/plugins/inspect/plugin-level.xml: + * docs/plugins/inspect/plugin-matroska.xml: + * docs/plugins/inspect/plugin-mulaw.xml: + * docs/plugins/inspect/plugin-multifile.xml: + * docs/plugins/inspect/plugin-multipart.xml: + * docs/plugins/inspect/plugin-navigationtest.xml: + * docs/plugins/inspect/plugin-oss4.xml: + * docs/plugins/inspect/plugin-ossaudio.xml: + * docs/plugins/inspect/plugin-png.xml: + * docs/plugins/inspect/plugin-pulseaudio.xml: + * docs/plugins/inspect/plugin-quicktime.xml: + * docs/plugins/inspect/plugin-replaygain.xml: + * docs/plugins/inspect/plugin-rtp.xml: + * docs/plugins/inspect/plugin-rtsp.xml: + * docs/plugins/inspect/plugin-shapewipe.xml: + * docs/plugins/inspect/plugin-shout2send.xml: + * docs/plugins/inspect/plugin-smpte.xml: + * docs/plugins/inspect/plugin-soup.xml: + * docs/plugins/inspect/plugin-spectrum.xml: + * docs/plugins/inspect/plugin-speex.xml: + * docs/plugins/inspect/plugin-taglib.xml: + * docs/plugins/inspect/plugin-udp.xml: + * docs/plugins/inspect/plugin-video4linux2.xml: + * docs/plugins/inspect/plugin-videobox.xml: + * docs/plugins/inspect/plugin-videocrop.xml: + * docs/plugins/inspect/plugin-videofilter.xml: + * docs/plugins/inspect/plugin-videomixer.xml: + * docs/plugins/inspect/plugin-wavenc.xml: + * docs/plugins/inspect/plugin-wavpack.xml: + * docs/plugins/inspect/plugin-wavparse.xml: + * docs/plugins/inspect/plugin-ximagesrc.xml: + * docs/plugins/inspect/plugin-y4menc.xml: + docs: update docs + +2011-01-11 23:39:12 +0530 Arun Raghavan + + * ext/pulse/pulsesink.c: + pulsesink: Make corking during pause synchronous + This makes the call to pa_stream_cork() during ringbuffer pause() + synchronous, which makes sure that the clock does not advance after we + take a snapshot for start_time. + https://bugzilla.gnome.org/show_bug.cgi?id=639240 + +2011-01-11 19:33:16 +0000 Tim-Philipp Müller + + * po/da.po: + * po/gl.po: + * po/pl.po: + * po/pt_BR.po: + * po/sl.po: + * po/sv.po: + * po/tr.po: + po: update translations + +2011-01-11 15:50:28 +0200 Stefan Kost + + * common: + Automatic update of common submodule + From e572c87 to f94d739 + +2011-01-10 16:36:19 +0000 Tim-Philipp Müller + + * common: + Automatic update of common submodule + From ccbaa85 to e572c87 + +2011-01-10 14:53:39 +0000 Tim-Philipp Müller + + * common: + Automatic update of common submodule + From 46445ad to ccbaa85 + +2011-01-07 13:24:02 +0000 Tim-Philipp Müller + + * configure.ac: + * win32/common/config.h: + 0.10.26.2 pre-release + +2011-01-07 13:06:38 +0000 Tim-Philipp Müller + + * po/af.po: + * po/az.po: + * po/bg.po: + * po/ca.po: + * po/cs.po: + * po/da.po: + * po/de.po: + * po/el.po: + * po/en_GB.po: + * po/es.po: + * po/eu.po: + * po/fi.po: + * po/fr.po: + * po/gl.po: + * po/hu.po: + * po/id.po: + * po/it.po: + * po/ja.po: + * po/lt.po: + * po/lv.po: + * po/mt.po: + * po/nb.po: + * po/nl.po: + * po/or.po: + * po/pl.po: + * po/pt_BR.po: + * po/ro.po: + * po/ru.po: + * po/sk.po: + * po/sl.po: + * po/sq.po: + * po/sr.po: + * po/sv.po: + * po/tr.po: + * po/uk.po: + * po/vi.po: + * po/zh_CN.po: + * po/zh_HK.po: + * po/zh_TW.po: + po: update translations + +2011-01-07 02:32:20 +0000 Tim-Philipp Müller + + * gst/alpha/gstalpha.c: + alpha: fix compiler warnings caused by -DG_DISABLE_ASSERT + +2011-01-07 02:06:51 +0000 Tim-Philipp Müller + + * gst/matroska/ebml-read.c: + matroska: don't put essential function calls into g_assert() + g_assert() will expand to NOOPs if -DG_DISABLE_ASSERT is passed. + +2011-01-07 01:35:45 +0000 Tim-Philipp Müller + + * sys/v4l2/gstv4l2sink.c: + v4l2sink: don't put functional code like ioctl calls into g_return_if_fail() + These macros will expand to NOOPs given the right defines. Also, + g_return_if_fail() and friends are meant to be used to catch programming + errors (like invalid input to functions), not runtime error handling. + +2011-01-07 01:11:02 +0000 Tim-Philipp Müller + + * tests/check/Makefile.am: + tests: never disable g_assert() and cast checks for the unit tests + The unit tests are riddled with g_assert() and friends, make sure we + don't disable assert and cast checks for the unit tests even if + this has been specified for the rest of the code base, e.g. via + --disable-glib-asserts. + +2011-01-06 12:29:21 +0100 Edward Hervey + + * gst/rtp/gstrtpmp4adepay.c: + rtp: Fix unitialized variables on macosx + +2011-01-06 12:28:58 +0100 Edward Hervey + + * gst/qtdemux/qtdemux_dump.c: + qtdemux: Fix unitialized variables on macosx + +2011-01-05 17:49:16 -0800 David Schleef + + * gst/debugutils/gstcapsdebug.c: + capsdebug: Add capdebug debug category + +2010-12-11 12:42:10 -0800 David Schleef + + * gst/deinterlace/gstdeinterlace.c: + deinterlace: Change the default to linear + The previous default, greedyh, takes 4 times as long as MPEG-2 + video decoding, and is unlikely fast enough on any current CPU + to play 1080i video in real-time. greedyl isn't much faster. + linear was chosen over vfir, since the quality advantage of vfir + is minimal compared to the occasional visual artifacts and slower + processing. + +2011-01-05 18:32:58 +0100 Wim Taymans + + * gst/rtsp/gstrtspsrc.c: + rtspsrc: don't confuse return values + Return a return value of the right type. + +2011-01-05 16:24:13 +0100 Edward Hervey + + * gst/qtdemux/qtdemux.c: + * gst/qtdemux/qtdemux_dump.c: + qtdemux: Fix unitialized variables on macosx + +2011-01-05 15:03:32 +0100 Wim Taymans + + * gst/rtp/gstrtpvrawdepay.c: + vrawdepay: fix length check + Add some more debugging. + Add the length check so we don't cause unneeded warnings. + +2011-01-05 12:04:03 +0100 Wim Taymans + + * gst/udp/gstmultiudpsink.c: + * gst/udp/gstmultiudpsink.h: + multiudpsink: add buffer-size property + Add buffer-size property to configure the kernel send buffer. + +2011-01-03 20:16:22 +0200 Stefan Kost + + * gst/rtsp/gstrtspsrc.c: + rtspsrc: remove unused variables when debug-logging disabled + +2011-01-03 20:06:35 +0200 Stefan Kost + + * gst/matroska/matroska-demux.c: + matroska-demux: remove unused variables when debug-logging disabled + +2011-01-03 18:05:15 +0100 Wim Taymans + + * ext/libcaca/gstcacasink.c: + cacasink: fix masks and strides + Use the right endianness to read the masks. + Use the right strides for the bitmap. + Fixes #638569 + +2011-01-03 01:18:06 +0000 Tim-Philipp Müller + + * sys/v4l2/gstv4l2src.c: + v4l2src: undo presumably accidental enablement of the GstXOverlay interface + Looks like this got enabled by accident when adding it to v4l2sink, + so undo this for now. Not sure it makes much sense in a GStreamer + context with current hardware. + +2011-01-03 15:40:11 +0100 Wim Taymans + + * gst/rtsp/gstrtspsrc.c: + rtspsrc: increase udp buffer size + Set a bigger UDP buffer size by default to reduce packet loss with + high bitrate streams. + +2011-01-02 19:19:27 -0800 David Schleef + + * gst/multifile/gstmultifilesink.c: + * gst/multifile/gstmultifilesink.h: + multifilesink: send stream headers in key-frame mode + +2011-01-02 19:43:02 +0000 Tim-Philipp Müller + + * ext/jack/Makefile.am: + * ext/jack/README: + * ext/jack/gstjack.c: + * ext/jack/gstjackaudiosink.c: + * ext/jack/gstjackaudiosrc.c: + jack: fix up element details and some other minor clean-ups + +2011-01-02 19:23:51 +0000 Erich Schubert + + * gst/id3demux/id3v2frames.c: + id3demux: fix parsing of ID3v2.4 genre frames with multiple genres + We'd only extract the first genre (multiple times) instead of all + genres. + https://bugzilla.gnome.org/show_bug.cgi?id=638535 + +2011-01-02 17:40:41 +0000 Tim-Philipp Müller + + * ext/jack/gstjackaudiosink.c: + * ext/jack/gstjackaudiosrc.c: + jack: template caps had lists with one value, just use value directly + +2011-01-02 17:07:19 +0000 Tim-Philipp Müller + + * ext/jack/gstjack.c: + * ext/jack/gstjackaudiosink.c: + * ext/jack/gstjackaudiosrc.c: + jack: make get_type functions thread-safe + Because we can (shouldn't be needed with other workarounds still there). + +2011-01-02 15:27:19 +0000 Tim-Philipp Müller + + * docs/plugins/gst-plugins-good-plugins.args: + * docs/plugins/gst-plugins-good-plugins.hierarchy: + * docs/plugins/gst-plugins-good-plugins.interfaces: + * docs/plugins/gst-plugins-good-plugins.prerequisites: + * docs/plugins/inspect/plugin-deinterlace.xml: + * docs/plugins/inspect/plugin-matroska.xml: + * docs/plugins/inspect/plugin-monoscope.xml: + * docs/plugins/inspect/plugin-rtp.xml: + docs: update plugin docs + +2011-01-02 15:25:41 +0000 Tim-Philipp Müller + + * .gitignore: + * configure.ac: + * docs/plugins/Makefile.am: + * docs/plugins/gst-plugins-good-plugins-docs.sgml: + * docs/plugins/gst-plugins-good-plugins-sections.txt: + * docs/plugins/inspect/plugin-jack.xml: + * ext/Makefile.am: + * gst-plugins-good.spec.in: + * tests/examples/Makefile.am: + * tests/examples/jack/Makefile.am: + jack: new jackaudiosrc and jackaudiosink elements, moved from gst-plugins-bad + https://bugzilla.gnome.org/show_bug.cgi?id=621929 + +2010-10-19 16:23:23 +0300 Stefan Kost + + * ext/jack/gstjackaudiosink.c: + * ext/jack/gstjackaudiosrc.c: + various (ext): add missing G_PARAM_STATIC_STRINGS flags + Canonicalize property names as needed. + +2010-09-09 14:49:06 -0400 Tristan Matthews + + * ext/jack/Makefile.am: + * ext/jack/gstjackaudiosink.c: + * ext/jack/gstjackaudiosrc.c: + jack: added translatable text for server not found error + +2010-09-06 17:17:54 -0400 Tristan Matthews + + * tests/examples/jack/Makefile.am: + * tests/examples/jack/jack_client.c: + examples: add test to demonstrate jack_client_t usage + +2010-09-06 16:11:31 -0400 Tristan Matthews + + * ext/jack/gstjack.c: + * ext/jack/gstjack.h: + * ext/jack/gstjackaudioclient.c: + * ext/jack/gstjackaudioclient.h: + * ext/jack/gstjackaudiosink.c: + * ext/jack/gstjackaudiosink.h: + * ext/jack/gstjackaudiosrc.c: + * ext/jack/gstjackaudiosrc.h: + jack: added client property + +2010-06-17 16:26:07 -0400 Tristan Matthews + + * ext/jack/gstjackbin.c: + jack: removed unused file gstjackbin.c + This is a 0.8 leftover. + +2010-05-13 12:55:29 +0200 Wim Taymans + + * ext/jack/gstjackaudiosrc.c: + jacksrc: make sure we always read nframes + Error out when we are asked to read a different size that what was configured as + the jack period size because that would mean something else is wrong. + Fixes #618409 + +2010-05-11 17:56:31 -0400 Tristan Matthews + + * ext/jack/gstjackaudiosrc.c: + * ext/jack/gstjackaudiosrc.h: + jack: improve process_cb + +2010-04-27 10:48:32 -0400 Tristan Matthews + + * ext/jack/Makefile.am: + * ext/jack/gstjackaudiosrc.c: + * ext/jack/gstjackutil.c: + * ext/jack/gstjackutil.h: + jack: implement multichannel support correctly for jackaudiosrc + Fixes parts of bug #616541. + +2010-04-27 11:21:16 +0300 Stefan Kost + + * ext/jack/gstjackaudiosink.c: + * ext/jack/gstjackaudiosrc.c: + * ext/jack/gstjackringbuffer.h: + jack: remove empty dispose and finalize methods + +2010-04-27 10:59:00 +0300 Stefan Kost + + * ext/jack/gstjackaudiosink.c: + * ext/jack/gstjackaudiosrc.c: + jack: don't leak caps + Add dispose methods to clear caps. + +2010-04-27 10:34:24 +0300 Stefan Kost + + * ext/jack/gstjackaudiosink.c: + * ext/jack/gstjackaudiosrc.c: + jack: don't use GST_DEBUG_FUNCPTR for gobject vmethods + +2010-03-24 15:59:53 +0200 Stefan Kost + + * ext/jack/gstjackaudiosrc.c: + jack: fix element name in section doc blob + +2010-03-22 16:56:03 +0100 Benjamin Otte + + * ext/jack/gstjackaudiosrc.c: + Add -Wold-style-definition + and fix the warnings + +2010-03-21 21:39:18 +0100 Benjamin Otte + + * ext/jack/gstjack.h: + Add -Wmissing-declarations -Wmissing-prototypes to configure flags + And fix all warnings + +2010-03-18 17:30:26 +0100 Benjamin Otte + + * ext/jack/gstjackaudiosink.c: + * ext/jack/gstjackaudiosrc.c: + gst_element_class_set_details => gst_element_class_set_details_simple + +2009-10-12 09:06:37 +0300 Stefan Kost + + * ext/jack/gstjackaudiosink.c: + * ext/jack/gstjackaudiosrc.c: + jack: ensure segtotal is at least 2 + Not only adjust buffer-time and avoid segtotal=0, but instead ensure segtotal is + atleast 2. Do same change on jacksrc. We could also check the latency and buffer + time configured by the client and adjust buffer-time so that we get to the same + number of segments. + +2009-10-12 00:51:27 +0300 Stefan Kost + + * ext/jack/gstjackaudiosink.c: + jack: don't crash in ringbuffer with SIGFPE on small buffer-times + Jack overrides user-specified latency-time with the one it gets from jack + itself. It also needs to adjust buffer-time somewhat to avoid segtotal being 0 + +2009-05-11 16:12:54 +0300 Stefan Kost + + * ext/jack/gstjackaudioclient.c: + * ext/jack/gstjackaudiosink.c: + jack: when stopping playback, do one more cycle to flush the port. Fixes #582167 + The gst_jack_audio_client_set_active() flags the port as deactivating and uses + a GCond to wait until the jack_process_cb() has run once more and cleared the + flag. This way the client zero's the buffer. This happens if one manyally go + to PAUSED and then to READY, while leting the mainloop run inbetween. + +2009-03-16 11:21:02 +0100 Wim Taymans + + * ext/jack/gstjack.c: + * ext/jack/gstjack.h: + * ext/jack/gstjackaudiosink.c: + * ext/jack/gstjackaudiosrc.c: + jack: Add new connection mode + Add a new connection mode to jacksrc and jacksink. In this new auto-force + connection mode jack will create as many ports as requested/needed in the + pipeline and will then connect as many physical ports as possible, possibly + leaving some ports unconnected. + Also get rid of some leftover g_print. + Fixes #575284. + +2008-11-23 17:50:08 +0000 Stefan Kost + + ext/jack/: Query port latencies for sink/src delays. + Original commit message from CVS: + * ext/jack/gstjackaudiosink.c: + * ext/jack/gstjackaudiosrc.c: + Query port latencies for sink/src delays. + * ext/jack/gstjackbin.c: + No printf please. + +2008-11-04 12:42:30 +0000 Stefan Kost + + Don't install static libs for plugins. Fixes #550851 for -bad. + Original commit message from CVS: + * ext/alsaspdif/Makefile.am: + * ext/amrwb/Makefile.am: + * ext/apexsink/Makefile.am: + * ext/arts/Makefile.am: + * ext/artsd/Makefile.am: + * ext/audiofile/Makefile.am: + * ext/audioresample/Makefile.am: + * ext/bz2/Makefile.am: + * ext/cdaudio/Makefile.am: + * ext/celt/Makefile.am: + * ext/dc1394/Makefile.am: + * ext/dirac/Makefile.am: + * ext/directfb/Makefile.am: + * ext/divx/Makefile.am: + * ext/dts/Makefile.am: + * ext/faac/Makefile.am: + * ext/faad/Makefile.am: + * ext/gsm/Makefile.am: + * ext/hermes/Makefile.am: + * ext/ivorbis/Makefile.am: + * ext/jack/Makefile.am: + * ext/jp2k/Makefile.am: + * ext/ladspa/Makefile.am: + * ext/lcs/Makefile.am: + * ext/libfame/Makefile.am: + * ext/libmms/Makefile.am: + * ext/metadata/Makefile.am: + * ext/mpeg2enc/Makefile.am: + * ext/mplex/Makefile.am: + * ext/musepack/Makefile.am: + * ext/musicbrainz/Makefile.am: + * ext/mythtv/Makefile.am: + * ext/nas/Makefile.am: + * ext/neon/Makefile.am: + * ext/ofa/Makefile.am: + * ext/polyp/Makefile.am: + * ext/resindvd/Makefile.am: + * ext/sdl/Makefile.am: + * ext/shout/Makefile.am: + * ext/snapshot/Makefile.am: + * ext/sndfile/Makefile.am: + * ext/soundtouch/Makefile.am: + * ext/spc/Makefile.am: + * ext/swfdec/Makefile.am: + * ext/tarkin/Makefile.am: + * ext/theora/Makefile.am: + * ext/timidity/Makefile.am: + * ext/twolame/Makefile.am: + * ext/x264/Makefile.am: + * ext/xine/Makefile.am: + * ext/xvid/Makefile.am: + * gst-libs/gst/app/Makefile.am: + * gst-libs/gst/dshow/Makefile.am: + * gst/aiffparse/Makefile.am: + * gst/app/Makefile.am: + * gst/audiobuffer/Makefile.am: + * gst/bayer/Makefile.am: + * gst/cdxaparse/Makefile.am: + * gst/chart/Makefile.am: + * gst/colorspace/Makefile.am: + * gst/dccp/Makefile.am: + * gst/deinterlace/Makefile.am: + * gst/deinterlace2/Makefile.am: + * gst/dvdspu/Makefile.am: + * gst/festival/Makefile.am: + * gst/filter/Makefile.am: + * gst/flacparse/Makefile.am: + * gst/flv/Makefile.am: + * gst/games/Makefile.am: + * gst/h264parse/Makefile.am: + * gst/librfb/Makefile.am: + * gst/mixmatrix/Makefile.am: + * gst/modplug/Makefile.am: + * gst/mpeg1sys/Makefile.am: + * gst/mpeg4videoparse/Makefile.am: + * gst/mpegdemux/Makefile.am: + * gst/mpegtsmux/Makefile.am: + * gst/mpegvideoparse/Makefile.am: + * gst/mve/Makefile.am: + * gst/nsf/Makefile.am: + * gst/nuvdemux/Makefile.am: + * gst/overlay/Makefile.am: + * gst/passthrough/Makefile.am: + * gst/pcapparse/Makefile.am: + * gst/playondemand/Makefile.am: + * gst/rawparse/Makefile.am: + * gst/real/Makefile.am: + * gst/rtjpeg/Makefile.am: + * gst/rtpmanager/Makefile.am: + * gst/scaletempo/Makefile.am: + * gst/sdp/Makefile.am: + * gst/selector/Makefile.am: + * gst/smooth/Makefile.am: + * gst/smoothwave/Makefile.am: + * gst/speed/Makefile.am: + * gst/speexresample/Makefile.am: + * gst/stereo/Makefile.am: + * gst/subenc/Makefile.am: + * gst/tta/Makefile.am: + * gst/vbidec/Makefile.am: + * gst/videodrop/Makefile.am: + * gst/videosignal/Makefile.am: + * gst/virtualdub/Makefile.am: + * gst/vmnc/Makefile.am: + * gst/y4m/Makefile.am: + * sys/acmenc/Makefile.am: + * sys/cdrom/Makefile.am: + * sys/dshowdecwrapper/Makefile.am: + * sys/dshowsrcwrapper/Makefile.am: + * sys/dvb/Makefile.am: + * sys/dxr3/Makefile.am: + * sys/fbdev/Makefile.am: + * sys/oss4/Makefile.am: + * sys/qcam/Makefile.am: + * sys/qtwrapper/Makefile.am: + * sys/vcd/Makefile.am: + * sys/wininet/Makefile.am: + * win32/common/config.h: + Don't install static libs for plugins. Fixes #550851 for -bad. + +2008-09-17 13:59:21 +0000 Jan Schmidt + + Fix compiler warnings on OS/X + Original commit message from CVS: + * ext/jack/gstjackaudiosink.c: (jack_process_cb): + * gst/rtpmanager/rtpjitterbuffer.c: (calculate_skew): + Fix compiler warnings on OS/X + +2008-08-07 13:15:21 +0000 Stefan Kost + + ext/jack/gstjackaudiosrc.c: Try committing this once again. Now properly renamed. + Original commit message from CVS: + * ext/jack/gstjackaudiosrc.c: + Try committing this once again. Now properly renamed. + +2008-08-07 09:09:44 +0000 Stefan Kost + + docs/plugins/: docs/plugins/inspect/plugin-jack.xml + Original commit message from CVS: + * docs/plugins/Makefile.am: + * docs/plugins/gst-plugins-bad-plugins-docs.sgml: + * docs/plugins/gst-plugins-bad-plugins-sections.txt: + * docs/plugins/gst-plugins-bad-plugins.args: + * docs/plugins/gst-plugins-bad-plugins.hierarchy: + * docs/plugins/gst-plugins-bad-plugins.interfaces: + * docs/plugins/gst-plugins-bad-plugins.prerequisites: + * docs/plugins/inspect/plugin-jack.xml + Add new element to docs. + * ext/jack/gstjack.h + Add missing file. + * ext/jack/gstjackaudiosrc.c: + * ext/jack/gstjackaudiosrc.h: + Rename jackaudiosrc to jack_audio_src. + +2008-08-07 08:47:40 +0000 Tristan Matthews + + ext/jack/: Add a jackaudiosrc. Refactor sink slightly for better code reuse. + Original commit message from CVS: + patch by: Tristan Matthews + * ext/jack/Makefile.am: + * ext/jack/gstjack.c: + * ext/jack/gstjackaudioclient.c: + * ext/jack/gstjackaudiosink.c: + * ext/jack/gstjackaudiosink.h: + * ext/jack/gstjackaudiosrc.c: + * ext/jack/gstjackaudiosrc.h: + * ext/jack/gstjackringbuffer.h: + Add a jackaudiosrc. Refactor sink slightly for better code reuse. + Fixes #545197. + +2008-06-13 11:59:23 +0000 Stefan Kost + + docs/plugins/: docs/plugins/inspect/plugin-mythtv.xml + Original commit message from CVS: + * docs/plugins/Makefile.am: + * docs/plugins/gst-plugins-bad-plugins-docs.sgml: + * docs/plugins/gst-plugins-bad-plugins-sections.txt: + * docs/plugins/gst-plugins-bad-plugins.args: + * docs/plugins/gst-plugins-bad-plugins.hierarchy: + * docs/plugins/gst-plugins-bad-plugins.interfaces: + * docs/plugins/gst-plugins-bad-plugins.prerequisites: + * docs/plugins/gst-plugins-bad-plugins.signals: + * docs/plugins/inspect/plugin-alsaspdif.xml: + * docs/plugins/inspect/plugin-amrwb.xml: + * docs/plugins/inspect/plugin-app.xml: + * docs/plugins/inspect/plugin-bayer.xml: + * docs/plugins/inspect/plugin-bz2.xml: + * docs/plugins/inspect/plugin-cdaudio.xml: + * docs/plugins/inspect/plugin-cdxaparse.xml: + * docs/plugins/inspect/plugin-dtsdec.xml: + * docs/plugins/inspect/plugin-dvb.xml: + * docs/plugins/inspect/plugin-dvdspu.xml: + * docs/plugins/inspect/plugin-faac.xml: + * docs/plugins/inspect/plugin-faad.xml: + * docs/plugins/inspect/plugin-fbdevsink.xml: + * docs/plugins/inspect/plugin-festival.xml: + * docs/plugins/inspect/plugin-filter.xml: + * docs/plugins/inspect/plugin-flvdemux.xml: + * docs/plugins/inspect/plugin-freeze.xml: + * docs/plugins/inspect/plugin-gsm.xml: + * docs/plugins/inspect/plugin-gstinterlace.xml: + * docs/plugins/inspect/plugin-gstrtpmanager.xml: + * docs/plugins/inspect/plugin-h264parse.xml: + * docs/plugins/inspect/plugin-interleave.xml: + * docs/plugins/inspect/plugin-jack.xml: + * docs/plugins/inspect/plugin-ladspa.xml: + * docs/plugins/inspect/plugin-metadata.xml: + * docs/plugins/inspect/plugin-mms.xml: + * docs/plugins/inspect/plugin-modplug.xml: + * docs/plugins/inspect/plugin-mpeg2enc.xml: + * docs/plugins/inspect/plugin-mpeg4videoparse.xml: + * docs/plugins/inspect/plugin-mpegtsparse.xml: + * docs/plugins/inspect/plugin-mpegvideoparse.xml: + * docs/plugins/inspect/plugin-musepack.xml: + * docs/plugins/inspect/plugin-musicbrainz.xml: + * docs/plugins/inspect/plugin-mve.xml: + * docs/plugins/inspect/plugin-mythtv.xml + * docs/plugins/inspect/plugin-nas.xml: + * docs/plugins/inspect/plugin-neon.xml: + * docs/plugins/inspect/plugin-nsfdec.xml: + * docs/plugins/inspect/plugin-nuvdemux.xml: + * docs/plugins/inspect/plugin-oss4.xml + * docs/plugins/inspect/plugin-rawparse.xml: + * docs/plugins/inspect/plugin-real.xml: + * docs/plugins/inspect/plugin-replaygain.xml: + * docs/plugins/inspect/plugin-rfbsrc.xml: + * docs/plugins/inspect/plugin-sdl.xml: + * docs/plugins/inspect/plugin-sdp.xml: + * docs/plugins/inspect/plugin-selector.xml: + * docs/plugins/inspect/plugin-sndfile.xml: + * docs/plugins/inspect/plugin-soundtouch.xml: + * docs/plugins/inspect/plugin-spcdec.xml: + * docs/plugins/inspect/plugin-speed.xml: + * docs/plugins/inspect/plugin-speexresample.xml: + * docs/plugins/inspect/plugin-stereo.xml: + * docs/plugins/inspect/plugin-subenc.xml + * docs/plugins/inspect/plugin-timidity.xml: + * docs/plugins/inspect/plugin-tta.xml: + * docs/plugins/inspect/plugin-vcdsrc.xml: + * docs/plugins/inspect/plugin-videosignal.xml: + * docs/plugins/inspect/plugin-vmnc.xml: + * docs/plugins/inspect/plugin-wildmidi.xml: + * docs/plugins/inspect/plugin-x264.xml: + * docs/plugins/inspect/plugin-xvid.xml: + * docs/plugins/inspect/plugin-y4menc.xml: + * ext/amrwb/gstamrwbdec.c: + * ext/amrwb/gstamrwbenc.c: + * ext/amrwb/gstamrwbparse.c: + * ext/dc1394/gstdc1394.c: + * ext/directfb/dfbvideosink.c: + * ext/ivorbis/vorbisdec.c: + * ext/jack/gstjackaudiosink.c: + * ext/mpeg2enc/gstmpeg2enc.cc: + * ext/mplex/gstmplex.cc: + * ext/musicbrainz/gsttrm.c: + * ext/mythtv/gstmythtvsrc.c: + * ext/theora/theoradec.c: + * ext/timidity/gsttimidity.c: + * ext/timidity/gstwildmidi.c: + * gst-libs/gst/app/gstappsink.c: + * gst/deinterlace/gstdeinterlace.c: + * gst/dvdspu/gstdvdspu.c: + * gst/festival/gstfestival.c: + * gst/freeze/gstfreeze.c: + * gst/interleave/deinterleave.c: + * gst/interleave/interleave.c: + * gst/modplug/gstmodplug.cc: + * gst/nuvdemux/gstnuvdemux.c: + Add missing elements to docs. Fix doc-markup: use convinience syntax + for examples (produces valid docbook), add several refsec2 when we + have several titles. Fix some types. + +2008-06-12 14:49:18 +0000 Stefan Kost + + Do not use short_description in section docs for elements. We extract them from element details and there will be war... + Original commit message from CVS: + * ext/dc1394/gstdc1394.c: + * ext/ivorbis/vorbisdec.c: + * ext/jack/gstjackaudiosink.c: + * ext/metadata/gstmetadatademux.c: + * ext/mythtv/gstmythtvsrc.c: + * ext/theora/theoradec.c: + * gst-libs/gst/app/gstappsink.c: + * gst/bayer/gstbayer2rgb.c: + * gst/deinterlace/gstdeinterlace.c: + * gst/rawparse/gstaudioparse.c: + * gst/rawparse/gstvideoparse.c: + * gst/rtpmanager/gstrtpbin.c: + * gst/rtpmanager/gstrtpclient.c: + * gst/rtpmanager/gstrtpjitterbuffer.c: + * gst/rtpmanager/gstrtpptdemux.c: + * gst/rtpmanager/gstrtpsession.c: + * gst/rtpmanager/gstrtpssrcdemux.c: + * gst/selector/gstinputselector.c: + * gst/selector/gstoutputselector.c: + * gst/videosignal/gstvideoanalyse.c: + * gst/videosignal/gstvideodetect.c: + * gst/videosignal/gstvideomark.c: + * sys/oss4/oss4-mixer.c: + * sys/oss4/oss4-sink.c: + * sys/oss4/oss4-source.c: + Do not use short_description in section docs for elements. We extract + them from element details and there will be warnings if they differ. + Also fixing up the ChangeLog order. + +2008-05-26 17:52:21 +0000 Wim Taymans + + ext/jack/gstjackaudiosink.c: Include the element name in the port name to avoid duplicate port names. + Original commit message from CVS: + * ext/jack/gstjackaudiosink.c: + (gst_jack_audio_sink_allocate_channels): + Include the element name in the port name to avoid duplicate port names. + +2008-04-06 20:18:16 +0000 Tim-Philipp Müller + + ext/jack/gstjackaudiosink.c: Work around missing bits of thread-safety on older GLibs some more to avoid assertions w... + Original commit message from CVS: + * ext/jack/gstjackaudiosink.c: (gst_jack_audio_sink_class_init): + Work around missing bits of thread-safety on older GLibs some + more to avoid assertions when starting up multiple playbin + objects concurrently (see #512382). + +2008-03-13 14:25:20 +0000 Sebastian Dröge + + Use GST_LICENSE, GST_PACKAGE_NAME and GST_PACKAGE_ORIGIN instead of hardcoding values where possible. Fixes bug #522212. + Original commit message from CVS: + * ext/alsaspdif/alsaspdifsink.c: + * ext/gsm/gstgsm.c: + * ext/jack/gstjack.c: + * ext/libmms/gstmms.c: + * ext/neon/gstneonhttpsrc.c: + * ext/shout/gstshout.c: + * ext/timidity/gsttimidity.c: + * ext/timidity/gstwildmidi.c: + * gst/nuvdemux/gstnuvdemux.c: + * gst/tta/gsttta.c: + Use GST_LICENSE, GST_PACKAGE_NAME and GST_PACKAGE_ORIGIN instead + of hardcoding values where possible. Fixes bug #522212. + +2007-07-18 07:42:47 +0000 Stefan Kost + + ext/jack/gstjackaudiosink.c: Add stdlib include here too. + Original commit message from CVS: + * ext/jack/gstjackaudiosink.c: (gst_jack_ring_buffer_open_device), + (gst_jack_ring_buffer_acquire): + Add stdlib include here too. + +2007-04-04 07:36:28 +0000 Stefan Kost + + ext/jack/gstjackaudiosink.c: Try t better name clients. properly handle return codes when re- establishing links. + Original commit message from CVS: + * ext/jack/gstjackaudiosink.c: (gst_jack_ring_buffer_open_device), + (gst_jack_ring_buffer_acquire): + Try t better name clients. properly handle return codes when re- + establishing links. + +2007-03-18 17:57:48 +0000 Paul Davis + + ext/jack/gstjackaudioclient.c: Don't need to take the connection lock, it will not be used and could cause deadlocks. + Original commit message from CVS: + Based on patch by: Paul Davis + * ext/jack/gstjackaudioclient.c: (gst_jack_audio_unref_connection): + Don't need to take the connection lock, it will not be used and could + cause deadlocks. + +2007-03-08 15:24:52 +0000 Paul Davis + + ext/jack/: Make an object to manage client connections to the jack server which we will use in the future to run sele... + Original commit message from CVS: + Includes patch by: Paul Davis + * ext/jack/Makefile.am: + * ext/jack/gstjackaudioclient.c: (gst_jack_audio_client_init), + (jack_process_cb), (jack_sample_rate_cb), (jack_buffer_size_cb), + (jack_shutdown_cb), (connection_find), + (gst_jack_audio_make_connection), (gst_jack_audio_get_connection), + (gst_jack_audio_unref_connection), + (gst_jack_audio_connection_add_client), + (gst_jack_audio_connection_remove_client), + (gst_jack_audio_client_new), (gst_jack_audio_client_free), + (gst_jack_audio_client_get_client), + (gst_jack_audio_client_set_active): + * ext/jack/gstjackaudioclient.h: + Make an object to manage client connections to the jack server which we + will use in the future to run selected jack elements with the same jack + connection. + Make some stuff a bit more threadsafe. + Activate the jack client ASAP. + * ext/jack/gstjackaudiosink.c: + (gst_jack_audio_sink_allocate_channels), + (gst_jack_audio_sink_free_channels), (jack_process_cb), + (gst_jack_ring_buffer_open_device), + (gst_jack_ring_buffer_close_device), + (gst_jack_ring_buffer_acquire), (gst_jack_ring_buffer_release), + (gst_jack_audio_sink_class_init), (gst_jack_audio_sink_init), + (gst_jack_audio_sink_getcaps): + * ext/jack/gstjackaudiosink.h: + Use new client object to manage connections. + Don't remove and recreate all ports, try to reuse them. + +2007-01-12 10:25:40 +0000 Wim Taymans + + ext/jack/gstjackaudiosink.*: Improve docs. + Original commit message from CVS: + * ext/jack/gstjackaudiosink.c: (jack_sample_rate_cb), + (jack_buffer_size_cb), (jack_shutdown_cb), + (gst_jack_ring_buffer_acquire): + * ext/jack/gstjackaudiosink.h: + Improve docs. + +2006-12-06 16:57:17 +0000 Jan Schmidt + + ext/jack/.cvsignore: Ignore old files as requested by the build slave. + Original commit message from CVS: + * ext/jack/.cvsignore: + Ignore old files as requested by the build slave. + +2006-11-30 11:59:04 +0000 Wim Taymans + + ext/Makefile.am: Fix build. + Original commit message from CVS: + * ext/Makefile.am: + Fix build. + * ext/jack/gstjackaudiosink.c: (jack_process_cb), + (jack_sample_rate_cb), (jack_buffer_size_cb), (jack_shutdown_cb), + (gst_jack_ring_buffer_acquire): + Small cleanups. + +2006-11-30 11:49:36 +0000 Wim Taymans + + Added fully functional jackaudiosink. + Original commit message from CVS: + * configure.ac: + * ext/Makefile.am: + * ext/jack/Makefile.am: + * ext/jack/gstjack.c: (plugin_init): + * ext/jack/gstjack.h: + * ext/jack/gstjackaudiosink.c: (gst_jack_ring_buffer_get_type), + (gst_jack_ring_buffer_class_init), (jack_process_cb), + (jack_sample_rate_cb), (jack_buffer_size_cb), (jack_shutdown_cb), + (gst_jack_ring_buffer_init), (gst_jack_ring_buffer_dispose), + (gst_jack_ring_buffer_finalize), + (gst_jack_ring_buffer_open_device), + (gst_jack_ring_buffer_close_device), + (gst_jack_ring_buffer_acquire), (gst_jack_ring_buffer_release), + (gst_jack_ring_buffer_start), (gst_jack_ring_buffer_pause), + (gst_jack_ring_buffer_stop), (gst_jack_ring_buffer_delay), + (gst_jack_connect_get_type), (gst_jack_audio_sink_base_init), + (gst_jack_audio_sink_class_init), (gst_jack_audio_sink_init), + (gst_jack_audio_sink_set_property), + (gst_jack_audio_sink_get_property), (gst_jack_audio_sink_getcaps), + (gst_jack_audio_sink_create_ringbuffer): + * ext/jack/gstjackaudiosink.h: + Added fully functional jackaudiosink. + +2006-04-08 21:48:01 +0000 Stefan Kost + + Fix #337365 (g_type_class_ref <-> g_type_class_peek_parent) + Original commit message from CVS: + * ext/amrwb/gstamrwbdec.c: (gst_amrwbdec_class_init): + * ext/amrwb/gstamrwbenc.c: (gst_amrwbenc_class_init): + * ext/amrwb/gstamrwbparse.c: (gst_amrwbparse_class_init): + * ext/arts/gst_arts.c: (gst_arts_class_init): + * ext/artsd/gstartsdsink.c: (gst_artsdsink_class_init): + * ext/audiofile/gstafsink.c: (gst_afsink_class_init): + * ext/audiofile/gstafsrc.c: (gst_afsrc_class_init): + * ext/audioresample/gstaudioresample.c: + * ext/cdaudio/gstcdaudio.c: (gst_cdaudio_class_init): + * ext/directfb/dfbvideosink.c: (gst_dfbvideosink_class_init): + * ext/divx/gstdivxdec.c: (gst_divxdec_class_init): + * ext/hermes/gsthermescolorspace.c: + (gst_hermes_colorspace_class_init): + * ext/ivorbis/vorbisfile.c: (gst_ivorbisfile_class_init): + * ext/jack/gstjack.c: (gst_jack_class_init): + * ext/jack/gstjackbin.c: (gst_jack_bin_class_init): + * ext/lcs/gstcolorspace.c: (gst_colorspace_class_init): + * ext/libfame/gstlibfame.c: (gst_fameenc_class_init): + * ext/musicbrainz/gsttrm.c: (gst_musicbrainz_class_init): + * ext/nas/nassink.c: (gst_nassink_class_init): + * ext/shout/gstshout.c: (gst_icecastsend_class_init): + * ext/snapshot/gstsnapshot.c: (gst_snapshot_class_init): + * ext/sndfile/gstsf.c: (gst_sf_class_init): + * ext/swfdec/gstswfdec.c: (gst_swfdecbuffer_class_init), + (gst_swfdec_class_init): + * ext/tarkin/gsttarkindec.c: (gst_tarkindec_class_init): + * ext/tarkin/gsttarkinenc.c: (gst_tarkinenc_class_init): + * gst/cdxaparse/gstcdxastrip.c: (gst_cdxastrip_class_init): + * gst/chart/gstchart.c: (gst_chart_class_init): + * gst/colorspace/gstcolorspace.c: (gst_colorspace_class_init): + * gst/deinterlace/gstdeinterlace.c: (gst_deinterlace_class_init): + * gst/festival/gstfestival.c: (gst_festival_class_init): + * gst/filter/gstbpwsinc.c: (gst_bpwsinc_class_init): + * gst/filter/gstiir.c: (gst_iir_class_init): + * gst/filter/gstlpwsinc.c: (gst_lpwsinc_class_init): + * gst/librfb/gstrfbsrc.c: (gst_rfbsrc_class_init): + * gst/mixmatrix/mixmatrix.c: (gst_mixmatrix_class_init): + * gst/mpeg1sys/gstmpeg1systemencode.c: + (gst_system_encode_class_init): + * gst/mpeg1videoparse/gstmp1videoparse.c: + (gst_mp1videoparse_class_init): + * gst/mpeg2sub/gstmpeg2subt.c: (gst_mpeg2subt_class_init): + * gst/mpegaudioparse/gstmpegaudioparse.c: + (gst_mp3parse_class_init): + * gst/overlay/gstoverlay.c: (gst_overlay_class_init): + * gst/passthrough/gstpassthrough.c: (passthrough_class_init): + * gst/playondemand/gstplayondemand.c: (play_on_demand_class_init): + * gst/rtjpeg/gstrtjpegdec.c: (gst_rtjpegdec_class_init): + * gst/rtjpeg/gstrtjpegenc.c: (gst_rtjpegenc_class_init): + * gst/smooth/gstsmooth.c: (gst_smooth_class_init): + * gst/smoothwave/gstsmoothwave.c: (gst_smoothwave_class_init): + * gst/spectrum/gstspectrum.c: (gst_spectrum_class_init): + * gst/stereo/gststereo.c: (gst_stereo_class_init): + * gst/switch/gstswitch.c: (gst_switch_class_init): + * gst/tta/gstttadec.c: (gst_tta_dec_class_init): + * gst/tta/gstttaparse.c: (gst_tta_parse_class_init): + * gst/vbidec/gstvbidec.c: (gst_vbidec_class_init): + * gst/videocrop/gstvideocrop.c: (gst_video_crop_class_init): + * gst/virtualdub/gstxsharpen.c: (gst_xsharpen_class_init): + * gst/y4m/gsty4mencode.c: (gst_y4mencode_class_init): + * sys/cdrom/gstcdplayer.c: (cdplayer_class_init): + * sys/directsound/gstdirectsoundsink.c: + (gst_directsoundsink_class_init): + * sys/dxr3/dxr3audiosink.c: (dxr3audiosink_class_init): + * sys/dxr3/dxr3spusink.c: (dxr3spusink_class_init): + * sys/dxr3/dxr3videosink.c: (dxr3videosink_class_init): + * sys/qcam/gstqcamsrc.c: (gst_qcamsrc_class_init): + * sys/v4l2/gstv4l2colorbalance.c: + (gst_v4l2_color_balance_channel_class_init): + * sys/v4l2/gstv4l2tuner.c: (gst_v4l2_tuner_channel_class_init), + (gst_v4l2_tuner_norm_class_init): + * sys/ximagesrc/ximagesrc.c: (gst_ximagesrc_class_init): + Fix #337365 (g_type_class_ref <-> g_type_class_peek_parent) + +2006-04-01 10:09:11 +0000 Thomas Vander Stichele + + * ext/jack/gstjack.c: + rework build; add translations for v4l2 + Original commit message from CVS: + rework build; add translations for v4l2 + +2005-10-12 14:29:55 +0000 Stefan Kost + + renamed GST_FLAGS macros to GST_OBJECT_FLAGS moved bitshift from macro to enum definition + Original commit message from CVS: + * examples/indexing/indexmpeg.c: (main): + * ext/artsd/gstartsdsink.c: (gst_artsdsink_open_audio), + (gst_artsdsink_close_audio), (gst_artsdsink_change_state): + * ext/artsd/gstartsdsink.h: + * ext/audiofile/gstafparse.c: (gst_afparse_open_file), + (gst_afparse_close_file): + * ext/audiofile/gstafparse.h: + * ext/audiofile/gstafsink.c: (gst_afsink_open_file), + (gst_afsink_close_file), (gst_afsink_chain), + (gst_afsink_change_state): + * ext/audiofile/gstafsink.h: + * ext/audiofile/gstafsrc.c: (gst_afsrc_open_file), + (gst_afsrc_close_file), (gst_afsrc_change_state): + * ext/audiofile/gstafsrc.h: + * ext/cdaudio/gstcdaudio.c: (gst_cdaudio_init): + * ext/directfb/directfbvideosink.c: (gst_directfbvideosink_init): + * ext/dts/gstdtsdec.c: (gst_dtsdec_init): + * ext/jack/gstjack.h: + * ext/jack/gstjackbin.c: (gst_jack_bin_init), + (gst_jack_bin_change_state): + * ext/musepack/gstmusepackdec.c: (gst_musepackdec_init): + * ext/musicbrainz/gsttrm.c: (gst_musicbrainz_init): + * ext/nas/nassink.c: (gst_nassink_open_audio), + (gst_nassink_close_audio), (gst_nassink_change_state): + * ext/nas/nassink.h: + * ext/polyp/polypsink.c: (gst_polypsink_init): + * ext/sdl/sdlvideosink.c: (gst_sdlvideosink_change_state): + * ext/sdl/sdlvideosink.h: + * ext/smoothwave/gstsmoothwave.c: (gst_smoothwave_init): + * ext/sndfile/gstsf.c: (gst_sf_set_property), + (gst_sf_change_state), (gst_sf_release_request_pad), + (gst_sf_open_file), (gst_sf_close_file), (gst_sf_loop): + * ext/sndfile/gstsf.h: + * ext/swfdec/gstswfdec.c: (gst_swfdec_init): + * ext/tarkin/gsttarkindec.c: (gst_tarkindec_init): + * gst/apetag/apedemux.c: (gst_ape_demux_init): + * gst/cdxaparse/gstcdxaparse.c: (gst_cdxaparse_init): + * gst/cdxaparse/gstcdxastrip.c: (gst_cdxastrip_init): + * gst/festival/gstfestival.c: (gst_festival_change_state): + * gst/festival/gstfestival.h: + * gst/mpeg2sub/gstmpeg2subt.c: (gst_mpeg2subt_init): + * gst/multifilesink/gstmultifilesink.c: (gst_multifilesink_init), + (gst_multifilesink_set_location), (gst_multifilesink_open_file), + (gst_multifilesink_close_file), (gst_multifilesink_next_file), + (gst_multifilesink_pad_query), (gst_multifilesink_handle_event), + (gst_multifilesink_chain), (gst_multifilesink_change_state): + * gst/multifilesink/gstmultifilesink.h: + * gst/videodrop/gstvideodrop.c: (gst_videodrop_init): + * sys/cdrom/gstcdplayer.c: (cdplayer_init): + * sys/dxr3/dxr3audiosink.c: (dxr3audiosink_init), + (dxr3audiosink_open), (dxr3audiosink_close), + (dxr3audiosink_chain_pcm), (dxr3audiosink_chain_ac3), + (dxr3audiosink_change_state): + * sys/dxr3/dxr3audiosink.h: + * sys/dxr3/dxr3spusink.c: (dxr3spusink_init), (dxr3spusink_open), + (dxr3spusink_close), (dxr3spusink_chain), + (dxr3spusink_change_state): + * sys/dxr3/dxr3spusink.h: + * sys/dxr3/dxr3videosink.c: (dxr3videosink_init), + (dxr3videosink_open), (dxr3videosink_close), + (dxr3videosink_write_data), (dxr3videosink_change_state): + * sys/dxr3/dxr3videosink.h: + * sys/glsink/glimagesink.c: (gst_glimagesink_init): + * sys/qcam/gstqcamsrc.c: (gst_qcamsrc_change_state), + (gst_qcamsrc_open), (gst_qcamsrc_close): + * sys/qcam/gstqcamsrc.h: + * sys/v4l2/gstv4l2src.c: (gst_v4l2src_init): + * sys/vcd/vcdsrc.c: (gst_vcdsrc_set_property), (gst_vcdsrc_get), + (gst_vcdsrc_open_file), (gst_vcdsrc_close_file), + (gst_vcdsrc_change_state), (gst_vcdsrc_recalculate): + * sys/vcd/vcdsrc.h: + renamed GST_FLAGS macros to GST_OBJECT_FLAGS + moved bitshift from macro to enum definition + +2005-09-05 17:20:29 +0000 Jan Schmidt + + * ext/jack/gstjack.c: + * ext/jack/gstjackbin.c: + Fix up all the state change functions. + Original commit message from CVS: + Fix up all the state change functions. + +2004-08-03 14:28:12 +0000 Benjamin Otte + + fixes for G_DISABLE_ASSERT and friends + Original commit message from CVS: + * examples/dynparams/filter.c: (ui_control_create): + * examples/gstplay/player.c: (print_tag): + * ext/alsa/gstalsa.c: (gst_alsa_request_new_pad): + * ext/gdk_pixbuf/gstgdkanimation.c: + (gst_gdk_animation_iter_may_advance): + * ext/jack/gstjack.c: (gst_jack_request_new_pad): + * ext/mad/gstid3tag.c: (gst_mad_id3_to_tag_list), + (tag_list_to_id3_tag_foreach), (gst_id3_tag_handle_event): + * ext/vorbis/oggvorbisenc.c: (gst_oggvorbisenc_get_tag_value): + * ext/vorbis/vorbisenc.c: (gst_vorbisenc_get_tag_value): + * ext/xine/xineaudiodec.c: (gst_xine_audio_dec_chain): + * gst-libs/gst/media-info/media-info-test.c: (print_tag): + * gst/sine/demo-dparams.c: (main): + * gst/tags/gstvorbistag.c: (gst_tag_to_vorbis_comments): + * testsuite/alsa/formats.c: (create_pipeline): + * testsuite/alsa/sinesrc.c: (sinesrc_force_caps), (sinesrc_get): + fixes for G_DISABLE_ASSERT and friends + * gst/typefind/gsttypefindfunctions.c: (aac_type_find), + (mp3_type_frame_length_from_header), (mp3_type_find), + (plugin_init): + require mp3 typefinding to have at least MIN_HEADERS valid headers + add typefinding for AAC adts files + +2004-05-21 23:28:57 +0000 Stéphane Loeuillet + + * ext/jack/gstjack.c: + * ext/jack/gstjack.h: + second batch : remove ',' at end of enums as they could confuse older gcc, foreign compilers (forte) and gtk-doc (in ... + Original commit message from CVS: + second batch : + remove ',' at end of enums as they could confuse older gcc, foreign compilers (forte) and gtk-doc + (in gst-plugins/ext/ this time) + +2004-03-15 19:32:27 +0000 Thomas Vander Stichele + + * ext/jack/gstjack.c: + * ext/jack/gstjackbin.c: + don't mix tabs and spaces + Original commit message from CVS: + don't mix tabs and spaces + +2004-03-15 16:32:54 +0000 Johan Dahlin + + *.h: Revert indenting + Original commit message from CVS: + * *.h: Revert indenting + +2004-03-14 22:34:33 +0000 Thomas Vander Stichele + + * ext/jack/gstjack.c: + * ext/jack/gstjack.h: + * ext/jack/gstjackbin.c: + gst-indent + Original commit message from CVS: + gst-indent + +2004-01-12 03:40:18 +0000 David Schleef + + * ext/jack/gstjack.c: + Remove all usage of gst_pad_get_caps(), and replace it with gst_pad_get_allowed_caps() or gst_pad_get_negotiated_cap(). + Original commit message from CVS: + Remove all usage of gst_pad_get_caps(), and replace it with + gst_pad_get_allowed_caps() or gst_pad_get_negotiated_cap(). + +2003-12-22 01:47:09 +0000 David Schleef + + * ext/jack/gstjack.c: + Merge CAPS branch + Original commit message from CVS: + Merge CAPS branch + +2003-12-13 16:59:51 +0000 Benjamin Otte + + * ext/jack/gstjackbin.c: + removed GST_*_CAST. Disabling of type checking is done in glib. + Original commit message from CVS: + removed GST_*_CAST. Disabling of type checking is done in glib. + +2003-12-04 10:37:38 +0000 Andy Wingo + + * ext/jack/gstjack.c: + remove copyright field from plugins + Original commit message from CVS: + remove copyright field from plugins + +2003-11-07 12:47:02 +0000 Ronald S. Bultje + + * ext/jack/gstjackbin.c: + Remove all config.h includes from header files, add it to each source file and remove duplicate config.h includes fro... + Original commit message from CVS: + Remove all config.h includes from header files, add it to each source file and remove duplicate config.h includes from several source files + +2003-11-01 23:43:13 +0000 Iain Holmes + + * ext/jack/gstjack.c: + Jack fixed too + Original commit message from CVS: + Jack fixed too + +2003-10-29 03:15:55 +0000 David Schleef + + * ext/jack/gstjack.h: + change gst/bytestream.h to gst/bytestream/bytestream.h + Original commit message from CVS: + change gst/bytestream.h to gst/bytestream/bytestream.h + +2003-10-28 20:52:41 +0000 Benjamin Otte + + * ext/jack/gstjack.h: + merge TYPEFIND branch. Major changes: + Original commit message from CVS: + merge TYPEFIND branch. Major changes: + - totally reworked type(find) system + - all typefind functions are in gst/typefind now + - more typefind functions then before + - some plugins might fail to compile now because I don't have them installed and they + a) require bytestream or + b) haven't had their typefind fixed. + Please fix those plugins and put the typefind functions into gst/typefind if they don't have dependencies + +2003-10-08 16:08:19 +0000 Andy Wingo + + * ext/jack/gstjack.c: + /GstBuffer/GstData/ in the API where you can pass events. Fix the plugins to deal with that. Fixes #113488. + Original commit message from CVS: + /GstBuffer/GstData/ in the API where you can pass events. Fix the plugins to deal with that. Fixes #113488. + +2003-10-01 13:14:50 +0000 Ronald S. Bultje + + * ext/jack/gstjack.h: + New typefind system: bytestream is now part of the core all plugins have been modified to use this new typefind syste... + Original commit message from CVS: + New typefind system: + * bytestream is now part of the core + * all plugins have been modified to use this new typefind system + * asf typefinding added + * mpeg video stream typefiding removed because it's broken + * duplicate typefind entries removed + * extra id3 typefinding added, because we've seen 4 types of files + (riff/wav, flac, vorbis, mp3) with id3 headers and each of these needs + to work. Instead, I've added an id3 element and let it redo typefiding + after the id3 header. this needs a hack because spider only typefinds + once. We can remove this hack once spider supports multiple typefinds. + * with all this, mp3 typefinding is semi-rewritten + * id3 typefinding in flac/vorbis is removed, it's no longer needed + * fixed spider and gst-typefind to use this, too. + * Other general cleanups + +2003-09-30 12:56:27 +0000 Andy Wingo + + * ext/jack/gstjack.c: + * ext/jack/gstjack.h: + * ext/jack/gstjackbin.c: + conform to the buffer-frames props entry -- much nicer now... + Original commit message from CVS: + conform to the buffer-frames props entry -- much nicer now... + +2003-08-10 00:01:58 +0000 David Schleef + + * ext/jack/Makefile.am: + Remove redundant plugindir definition + Original commit message from CVS: + Remove redundant plugindir definition + +2003-07-19 23:25:25 +0000 Leif Johnson + + * ext/jack/gstjack.c: + * ext/jack/gstjack.h: + + changes for new float caps without slope/intercept + some category changes for plugins + Original commit message from CVS: + + changes for new float caps without slope/intercept + + some category changes for plugins + +2003-07-06 20:49:52 +0000 Ronald S. Bultje + + * ext/jack/gstjack.c: + New mimetypes gone into effect today - this commit changes all old mimetypes over to the new mimetypes spec as descri... + Original commit message from CVS: + New mimetypes gone into effect today - this commit changes all old mimetypes over to the new mimetypes spec as described in the previous commit's document. Note: some plugins will break, some pipelines will break, expect HEAD to be broken or at least not 100% working for a few days, but don't forget to report bugs + +2003-07-01 02:27:06 +0000 David Schleef + + * ext/jack/gstjack.c: + fix type punning + Original commit message from CVS: + fix type punning + +2003-06-29 19:46:13 +0000 Benjamin Otte + + * ext/jack/gstjack.c: + * ext/jack/gstjackbin.c: + compatibility fix for new GST_DEBUG stuff. + Original commit message from CVS: + compatibility fix for new GST_DEBUG stuff. + Includes fixes for missing includes for config.h and unistd.h + I only ensured for plugins I can build that they work, so if some of them are still broken, you gotta fix them yourselves unfortunately. + +2003-06-13 21:21:17 +0000 Wim Taymans + + * ext/jack/gstjack.c: + Removed ugly caps fixed flag hack, will be done automatically in core soon + Original commit message from CVS: + Removed ugly caps fixed flag hack, will be done automatically in + core soon + +2003-03-04 15:34:20 +0000 Andy Wingo + + * ext/jack/gstjack.c: + * ext/jack/gstjack.h: + * ext/jack/gstjackbin.c: + update for the latest jack cvs and non-cothreaded gst scheduler + Original commit message from CVS: + update for the latest jack cvs and non-cothreaded gst scheduler + +2003-02-05 20:38:41 +0000 Jan Schmidt + + * ext/jack/gstjack.c: + Changed caps->fixed to use FLAG_SET + Original commit message from CVS: + Changed caps->fixed to use FLAG_SET + +2003-01-10 13:38:32 +0000 Thomas Vander Stichele + + * ext/jack/gstjack.c: + PadConnect -> PadLink + Original commit message from CVS: + PadConnect -> PadLink + +2003-01-10 10:22:25 +0000 Thomas Vander Stichele + + * ext/jack/gstjack.c: + another batch of connect->link fixes please let me know about issues and please refrain of making them yourself, so t... + Original commit message from CVS: + another batch of connect->link fixes + please let me know about issues + and please refrain of making them yourself, so that I don't spend double + the time resolving conflicts + +2002-12-08 14:50:10 +0000 Thomas Vander Stichele + + * ext/jack/Makefile.am: + parallel install fixes + Original commit message from CVS: + parallel install fixes + +2002-09-29 18:12:18 +0000 Andy Wingo + + * ext/jack/gstjack.c: + * ext/jack/gstjackbin.c: + licenses again + Original commit message from CVS: + licenses again + +2002-09-18 19:02:52 +0000 Christian Schaller + + * ext/jack/gstjack.c: + plugins part of license field patch + Original commit message from CVS: + plugins part of license field patch + +2002-09-10 09:31:40 +0000 Ronald S. Bultje + + * ext/jack/gstjack.c: + This updates all plugins to the new API for gst_pad_try_set_caps + Original commit message from CVS: + This updates all plugins to the new API for gst_pad_try_set_caps + +2002-09-09 23:27:38 +0000 Thomas Vander Stichele + + * ext/jack/gstjack.c: + removing warnings as approved by wim + Original commit message from CVS: + removing warnings as approved by wim + +2002-08-23 04:04:11 +0000 Andy Wingo + + * ext/jack/gstjack.c: + * ext/jack/gstjackbin.c: + fix jack input port connection + Original commit message from CVS: + fix jack input port connection + +2002-07-09 17:39:17 +0000 Andy Wingo + + * ext/jack/gstjack.c: + compile fixen, and prepare to move MAINTAINER_MODE to as-version.m4 + Original commit message from CVS: + compile fixen, and prepare to move MAINTAINER_MODE to as-version.m4 + +2002-07-02 23:35:07 +0000 Andy Wingo + + * ext/jack/gstjack.c: + * ext/jack/gstjackbin.c: + make jack work in all its full duplex glory + Original commit message from CVS: + make jack work in all its full duplex glory + +2002-06-12 03:32:02 +0000 Andy Wingo + + * ext/jack/gstjack.c: + * ext/jack/gstjackbin.c: + working jack elements (fixed a problem in upstream jack) random other fixen... + Original commit message from CVS: + * working jack elements (fixed a problem in upstream jack) + * random other fixen... + +2002-05-15 19:08:49 +0000 Steve Baker + + * ext/jack/gstjack.c: + use new bytestream api + Original commit message from CVS: + use new bytestream api + +2002-05-13 18:08:33 +0000 Andy Wingo + + * ext/jack/gstjack.c: + * ext/jack/gstjack.h: + * ext/jack/gstjackbin.c: + update to new jack api + Original commit message from CVS: + update to new jack api + +2002-05-05 19:39:17 +0000 Andy Wingo + + * ext/jack/gstjack.c: + add some includes + Original commit message from CVS: + add some includes + +2002-05-05 01:08:05 +0000 Andy Wingo + + * ext/jack/gstjack.c: + * ext/jack/gstjack.h: + * ext/jack/gstjackbin.c: + better initialization. it doesn't work over here, though. + Original commit message from CVS: + better initialization. it doesn't work over here, though. + +2002-05-04 21:38:56 +0000 Andy Wingo + + * ext/jack/gstjackbin.c: + a commit so that jack will build without errors on Uraeus's system ;) + Original commit message from CVS: + a commit so that jack will build without errors on Uraeus's system ;) + +2002-05-04 20:53:35 +0000 Andy Wingo + + * ext/jack/gstjack.c: + set caps once we know the sample rate of the system + Original commit message from CVS: + set caps once we know the sample rate of the system + +2002-05-04 18:57:44 +0000 Andy Wingo + + * ext/jack/gstjack.c: + * ext/jack/gstjack.h: + * ext/jack/gstjackbin.c: + some jack fixes, alsa touchups, and add rtp by default to the build if there are any problems building rtp, we're mov... + Original commit message from CVS: + some jack fixes, alsa touchups, and add rtp by default to the build + if there are any problems building rtp, we're moving it back to experimental ;) + +2002-04-20 21:42:51 +0000 Andy Wingo + + * ext/jack/gstjack.c: + a hack to work around intltool's brokenness a current check for mpeg2dec details->klass reorganizations an element br... + Original commit message from CVS: + * a hack to work around intltool's brokenness + * a current check for mpeg2dec + * details->klass reorganizations + * an element browser that uses details->klass + * separated cdxa parse out from the avi directory + +2002-04-16 17:14:05 +0000 Andy Wingo + + * ext/jack/Makefile.am: + * ext/jack/gstjack.c: + * ext/jack/gstjack.h: + * ext/jack/gstjackbin.c: + Finally we're on to a proper jack setup, with a specialized bin and elements that can only go in a jack bin. I had to... + Original commit message from CVS: + Finally we're on to a proper jack setup, with a specialized bin and elements + that can only go in a jack bin. I had to fix the parser first to do this, but + to run it, the syntax is like so: + gst-launch jackbin.( filesrc ! mad ! jacksink ) + But of course it's not fully functional yet. Sigh. + +2002-04-11 20:42:26 +0000 Andy Wingo + + * ext/jack/gstjack.c: + GstPadTemplate <-> gst_pad_template <-> GST_PAD_TEMPLATE same with *factory and typefind. + Original commit message from CVS: + GstPadTemplate <-> gst_pad_template <-> GST_PAD_TEMPLATE + same with *factory and typefind. + also, some -Werror fixes. + +2002-03-30 21:07:51 +0000 Andy Wingo + + * ext/jack/gstjack.c: + alphabetization fixen a jack caps fix + Original commit message from CVS: + * alphabetization fixen + * a jack caps fix + +2002-03-30 19:31:13 +0000 Andy Wingo + + * ext/jack/gstjack.c: + add notify back to filesrc, it's needed for MVC applications remove notify printouts from gst-launch cleanup in gst-p... + Original commit message from CVS: + * add notify back to filesrc, it's needed for MVC applications + * remove notify printouts from gst-launch + * cleanup in gst-plugins configure.ac + * some jack updates + * remove SELF_ITERATING flag in favor of SEF_SCHEDULABLE (not a clear name, + but it's what we have for the moment) + * improve parsing of request pad names, no more sscanf + * fixes to the fastscheduler Makefile.am + +2002-03-20 21:45:04 +0000 Andy Wingo + + * ext/jack/gstjack.c: + s/Gnome-Streamer/GStreamer/ + Original commit message from CVS: + s/Gnome-Streamer/GStreamer/ + +2002-03-19 04:10:06 +0000 Andy Wingo + + * ext/jack/Makefile.am: + * ext/jack/gstjack.c: + removal of //-style comments don't link plugins to core libs -- the versioning is done internally to the plugins with... + Original commit message from CVS: + * removal of //-style comments + * don't link plugins to core libs -- the versioning is done internally to the plugins with the plugin_info struct, + and symbol resolution is lazy, so we can always know if a plugin can be loaded by the plugin_info data. in theory. + +2002-03-19 01:39:43 +0000 Andy Wingo + + * ext/jack/Makefile.am: + s/@GST_PLUGIN_LDFLAGS@/$(GST_PLUGIN_LDFLAGS)/ @-substitued variables variables are defined as make variables automagi... + Original commit message from CVS: + s/@GST_PLUGIN_LDFLAGS@/$(GST_PLUGIN_LDFLAGS)/ + @-substitued variables variables are defined as make variables automagically, + and this gives the user the freedom to say make GST_PLUGIN_LDFLAGS=-myflag + +2002-03-18 04:41:35 +0000 Andy Wingo + + * ext/jack/Makefile.am: + * ext/jack/README: + * ext/jack/gstjack.c: + * ext/jack/gstjack.h: + s/gst_element_install_std_props/gst_element_class_install_std_props/ -- it just makes more sense that way added jack ... + Original commit message from CVS: + * s/gst_element_install_std_props/gst_element_class_install_std_props/ -- it just makes more sense that way + * added jack element, doesn't quite work right yet but i didn't want to lose the work -- it does build, register, + and attempt to run though + * imposed some restrictions on the naming of request pads to better allow for reverse parsing + * added '%s' to reverse parsing + * added new bin flag to indicate that it is self-iterating, and some lame code in gst-launch to test it out + * fixen on launch-gui + * added pkg-config stuff for the editor's libs + +2011-01-02 11:37:14 +0000 Tim-Philipp Müller + + * sys/v4l2/Makefile.am: + * sys/v4l2/gstv4l2.c: + * sys/v4l2/gstv4l2bufferpool.c: + * sys/v4l2/v4l2_calls.c: + v4l2: mark v4l2sink as experimental and build only if --enable-experimental is passed + It's not really of 'good' quality yet, but there's a lot of + code shared with v4l2src, so not so easy to move it elswhere. + https://bugzilla.gnome.org/show_bug.cgi?id=612244 + +2011-01-02 01:24:21 +0000 Tim-Philipp Müller + + * sys/v4l2/gstv4l2object.c: + * sys/v4l2/gstv4l2object.h: + * sys/v4l2/gstv4l2sink.c: + * sys/v4l2/gstv4l2tuner.c: + * sys/v4l2/gstv4l2tuner.h: + * sys/v4l2/v4l2_calls.c: + Revert "v4l2: add norm property" + This reverts commit 9e1d419d07337e6db2cc3936472be205ce927e54. + Reverting this since it adds unreviewed and bad API to v4l2src + (property of type enum, with seemingly random and unsorted values). + +2011-01-01 23:26:33 +0000 Tim-Philipp Müller + + * tools/.gitignore: + * tools/Makefile.am: + * tools/README.filterstamp: + * tools/filterstamp.sh: + * tools/gst-launch-ext-m.m: + * tools/gst-launch-ext.1.in: + * tools/gst-visualise-m.m: + * tools/gst-visualise.1.in: + tools: remove unused left-over directory + These are all in -base/tools. + +2010-12-31 13:57:05 +0100 Wim Taymans + + * gst/rtp/gstrtpmp4adepay.c: + * gst/rtp/gstrtpmp4adepay.h: + mp4adepay: improve timestamps on outgoing packets + Improve parsing of the samplerate. + Parse the framelen so that we can calculate timestamps. + When interpollate the incomming timestamp on outgoing buffers when there are + multiple subframes. + fixes #625825 + +2010-12-31 02:16:54 +0000 Tim-Philipp Müller + + * ext/cairo/gsttimeoverlay.c: + * gst/videofilter/gstvideobalance.c: + cairo, videofilter: use gst/math-compat.h header for rint + +2010-12-30 14:30:27 -0800 David Schleef + + * gst/videofilter/gstvideobalance.c: + videobalance: Check for HAVE_RINT instead + Also change M_PI to G_PI for giggles. + +2010-12-30 14:21:37 -0800 David Schleef + + * ext/cairo/gstcairorender.c: + cairo: Don't use #ifdefs inside macros + +2010-12-30 14:20:52 -0800 David Schleef + + * gst/audiofx/audiochebband.c: + * gst/audiofx/audiocheblimit.c: + * gst/audiofx/audiokaraoke.c: + * gst/audiofx/audiowsincband.c: + * gst/audiofx/audiowsinclimit.c: + * gst/effectv/gstop.c: + * gst/equalizer/gstiirequalizer.c: + * gst/goom/convolve_fx.c: + * gst/goom/ifs.c: + * gst/goom/lines.c: + * gst/goom/tentacle3d.c: + * tests/examples/audiofx/firfilter-example.c: + * tests/examples/audiofx/iirfilter-example.c: + Change M_PI to G_PI + +2010-12-30 12:07:52 -0800 David Schleef + + * gst/videofilter/gstvideobalance.c: + videobalance: use G_OS_WIN32 for windows check + +2010-12-30 16:24:16 +0100 Wim Taymans + + * gst/rtp/gstrtpmp4adepay.c: + mp4adepay: fix timestamps on buffers + +2010-12-30 16:22:48 +0100 Wim Taymans + + * gst/rtp/gstrtpmpvpay.c: + mpvpay: fix flushing and discont + Fix flushing and disconts. + Clean up in state changes. + +2010-12-29 23:38:18 +0000 Tim-Philipp Müller + + * gst/matroska/matroska-demux.c: + matroska-demux: increase allowed max. block size for push mode from 10M to 15M + It was an arbitrary limit from the start, meant as a basic sanity check, + so may just as well increase it a little. Would be good to provide + progress reporting while completing the block in any case.. + https://bugzilla.gnome.org/show_bug.cgi?id=637060 + +2010-12-29 23:09:04 +0000 Tim-Philipp Müller + + * gst/matroska/matroska-demux.c: + matroska-demux: assume matroska if no doctype is specified + https://bugzilla.gnome.org/show_bug.cgi?id=638019 + +2010-12-04 13:43:11 -0600 Rob Clark + + * sys/v4l2/gstv4l2object.c: + * sys/v4l2/gstv4l2object.h: + * sys/v4l2/gstv4l2sink.c: + * sys/v4l2/gstv4l2src.c: + * sys/v4l2/v4l2src_calls.c: + * sys/v4l2/v4l2src_calls.h: + v4l2: add interlaced support + +2010-10-02 14:45:14 -0500 Rob Clark + + * sys/v4l2/gstv4l2sink.c: + * sys/v4l2/gstv4l2sink.h: + * sys/v4l2/gstv4l2xoverlay.c: + * sys/v4l2/gstv4l2xoverlay.h: + v4l2sink: add navigation support + +2010-04-04 06:43:41 -0500 Rob Clark + + * sys/v4l2/gstv4l2object.c: + * sys/v4l2/gstv4l2object.h: + * sys/v4l2/gstv4l2sink.c: + * sys/v4l2/gstv4l2tuner.c: + * sys/v4l2/gstv4l2tuner.h: + * sys/v4l2/v4l2_calls.c: + v4l2: add norm property + Based on a patch by Guennadi Liakhovetski. + +2010-07-13 10:03:51 -0500 Rob Clark + + * sys/v4l2/gstv4l2sink.c: + * sys/v4l2/v4l2_calls.c: + * sys/v4l2/v4l2_calls.h: + v4l2: cleanup get/set input/output + output devices should use get/set output, and in either case we should + not print a warning message if the ioctl fails but the device does not + claim to support the tuner interface + +2010-06-10 11:15:46 -0500 Rob Clark + + * sys/v4l2/gstv4l2sink.c: + * sys/v4l2/gstv4l2xoverlay.c: + * sys/v4l2/gstv4l2xoverlay.h: + v4l2xoverlay: add support to create window + If xoverlay is available, v4l2sink should create a window for the overlay to + display in. + The window automatically tries to make itself as large as possible. + This works well on a small screen, but perhaps should first attempt to use + the size of the video that is played (no scaling). + +2010-04-04 06:41:28 -0500 Rob Clark + + * sys/v4l2/gstv4l2sink.c: + v4l2sink: special handling for cases gst_buffer_make_metadata_writable() + Special case check for sub-buffers: In certain cases, places like + GstBaseTransform, which might check that the buffer is writable before copying + metadata, timestamp, and such, will find that the buffer has more than one + reference to it. In these cases, they will create a sub-buffer with an offset=0 + and length equal to the original buffer size. + This could happen in two scenarios: (1) a tee in the pipeline, and (2) because + the refcnt is incremented in gst_mini_object_free() before the finalize function + is called, and decremented after it returns.. but returning this buffer to the + buffer pool in the finalize function, could wake up a thread blocked in + _buffer_alloc() which could run and get a buffer w/ refcnt==2 before the thread + originally unref'ing the buffer returns from finalize function and decrements + the refcnt back to 1! + This is related to issue #545501 + +2010-04-04 06:39:52 -0500 Rob Clark + + * sys/v4l2/gstv4l2bufferpool.c: + v4l2: fix race condition + The size of the buffer would be zero'd out in gst_v4l2_buffer_finalize() + after the buffer is qbuf'd or pushed onto the queue of available buffers.. + leaving a race condition where the thread waiting for the buffer could awake + and set back a valid size before the finalizing thread zeros out the length. + This would result that the newly allocated buffer has length of zero. + +2010-04-04 06:39:08 -0500 Rob Clark + + * sys/v4l2/gstv4l2sink.c: + * sys/v4l2/gstv4l2sink.h: + v4l2sink: add properties to control crop + +2010-04-04 06:37:16 -0500 Rob Clark + + * sys/v4l2/Makefile.am: + * sys/v4l2/gstv4l2object.c: + * sys/v4l2/gstv4l2sink.c: + * sys/v4l2/gstv4l2src.c: + * sys/v4l2/gstv4l2xoverlay.c: + v4l2: re-enable x-overlay support + +2010-12-25 11:52:36 -0600 Rob Clark + + * sys/v4l2/gstv4l2sink.c: + v4l2sink: fix for PAUSED->READY->PAUSED state transitions + When v4l2sink goes to PAUSED->READY it only stops streaming, so the state + should be set to STATE_PENDING_STREAMON in case the element transitions + back to PLAYING. + +2010-04-04 06:28:51 -0500 Rob Clark + + * sys/v4l2/gstv4l2sink.c: + * sys/v4l2/gstv4l2sink.h: + v4l2sink: add "min-queued-bufs" property + +2010-04-04 06:26:50 -0500 Rob Clark + + * sys/v4l2/gstv4l2bufferpool.c: + * sys/v4l2/gstv4l2bufferpool.h: + * sys/v4l2/gstv4l2sink.c: + * sys/v4l2/v4l2src_calls.c: + v4l2sink: Add support for blocking dequeue. + We'd prefer to throttle the decoder if we run out of buffers, to keep a bound + on memory usage. Also, for OMAP4 it is a requirement of the decoder to not + alternate between memory alloced by the display driver and malloc'd userspace + memory. + +2010-04-04 06:24:41 -0500 Rob Clark + + * sys/v4l2/gstv4l2bufferpool.c: + v4l2: clear flags before reusing buffer from buffer pool + note: this really only affects v4l2sink since gst_v4l2_buffer_pool_get() is + only called once per buffer in the v4l2src case (in + gst_v4l2src_buffer_pool_activate()) + +2010-04-04 06:23:31 -0500 Rob Clark + + * sys/v4l2/gstv4l2sink.c: + v4l2sink: don't render preroll buffers + Most v4l2 drivers will get upset when you queue the same buffer twice in a + row without first dequeueing it. + Rendering of pre-roll buffers can be re-introduced later, but will require + tracking the state of the buffer, and avoiding to re-QBUF if the buffer has + already been passed to the driver. + +2010-04-04 06:22:43 -0500 Rob Clark + + * sys/v4l2/gstv4l2sink.c: + v4l2sink: Improve behavior for shared buffers. + When the decoder is using pad_alloc(), v4l2sink would behave badly if + the number of buffers ('queue-size' property) was not high enough to + account for all the buffers needed by the decoder, and other elements + (such as queues) between the decoder and v4l2sink. This patch + slightly increases the default number of buffers, and changes v4l2sink + to drop frames rather than return an error in case the number of + buffers is not high enough. + +2010-11-15 15:58:28 +0100 Andy Wingo + + * ext/pulse/pulsesrc.c: + * ext/pulse/pulsesrc.h: + add "client" property + * ext/pulse/pulsesrc.c (gst_pulsesrc_class_init, gst_pulsesrc_init) + (gst_pulsesrc_set_property, gst_pulsesrc_get_property) + (gst_pulsesrc_open): Add a "client" property, as in pulsesink. + Fixes #634914 + +2010-12-29 15:54:46 +0000 Tim-Philipp Müller + + * gst/rtsp/gstrtspsrc.c: + rtspsrc: serialise/deserialise floats without changing locale + Use g_ascii_dtostr() and g_ascii_strtod() to serialise/deserialise + floating point numbers, instead of ugly hacks that switch locale + before and after calling libc functions (which is not a good idea + in a multi-threaded application). + +2010-12-29 14:40:05 +0000 Tim-Philipp Müller + + * gst/rtp/gstrtpjpegdepay.c: + rtpjpegdepay: fix framerate parsing for locales that use a comma as floating point + atof() converts strings according to the current locale, but the + framerate string will likely always use a dot as floating point + separator, so use g_ascii_strtod() instead (but also canonicalise + the string before, so we can handle both formats as input). + +2010-12-27 13:11:59 +0100 Wim Taymans + + * gst/rtpmanager/rtpsource.c: + rtpsource: use the right variable + Use the right variable for specifying that we sent a receiver report. + +2010-12-23 16:42:29 -0600 Rob Clark + + * sys/v4l2/gstv4l2bufferpool.c: + v4l2: fix typo + +2010-12-23 16:03:00 -0600 Rob Clark + + * gst/matroska/matroska-demux.c: + matroska-demux: add stream-format and alignment properties for h264 + +2010-12-22 11:41:59 +0100 Wim Taymans + + * gst/rtp/gstrtpgstpay.c: + gstpay: fix klass, add RTP as a use case + +2010-12-12 15:10:47 +0100 Wim Taymans + + * gst/rtp/gstrtpgstdepay.c: + gstdepay: cleanup the cache + +2010-12-12 05:10:01 +0100 Wim Taymans + + * gst/rtp/Makefile.am: + * gst/rtp/gstrtp.c: + * gst/rtp/gstrtpgstdepay.c: + * gst/rtp/gstrtpgstdepay.h: + * gst/rtp/gstrtpgstpay.c: + * gst/rtp/gstrtpgstpay.h: + gstpay/depay: add generic gstreamer payloader + Add the beginnings of a generic GStreamer buffers payloader. + +2010-12-23 17:06:58 +0100 Wim Taymans + + * gst/rtp/gstrtpmp4gpay.c: + mp4gpay: reset state on flush-stop + +2010-12-23 16:26:07 +0100 Wim Taymans + + * gst/rtp/gstrtpmp4gdepay.c: + mp4gdepay: flush state on flush-stop + +2010-12-23 16:25:15 +0100 Wim Taymans + + * gst/rtsp/gstrtspsrc.c: + rtspsrc: on-npt-stop is a manager signal + +2010-12-23 15:24:29 +0100 Wim Taymans + + * gst/rtsp/gstrtspsrc.c: + * gst/rtsp/gstrtspsrc.h: + rtspsrc: improve RTP session handling + Store the RTP session in the stream so that we can more efficiently + perform actions on the stream based on RTP signals. + +2010-12-23 13:55:31 +0100 Wim Taymans + + * gst/rtpmanager/rtpsource.c: + rtpsource: include last send RB block + Only report RB values for non-internal sources. + Report not only the RB blocks we last received from but also the last RB + block we sent to a source. + +2010-12-23 13:52:57 +0100 Wim Taymans + + * gst/rtpmanager/rtpsession.c: + * gst/rtpmanager/rtpsource.h: + rtpsession: remember last sent RB values. + +2010-12-23 13:00:49 +0100 Wim Taymans + + * gst/rtpmanager/rtpsource.c: + rtpsource: include all stats and document + Include all possible stats of a source in the stats structure because we might + be interested in what happened in the past. + Document the stats property and the fields. + +2010-12-23 12:59:59 +0100 Wim Taymans + + * tests/examples/rtp/client-PCMA.c: + examples: add example RTP stats + Add some more RTP examples for how to retrieve RTP stats in a receiver. + +2010-12-23 12:58:05 +0100 Wim Taymans + + * gst/rtpmanager/rtpsession.c: + rtpsession: also emit RTCP activity on SR + Also emit RTCP activity signals when we receive an SR packet without RB blocks, + such as from a sender that is not receiving anything. + +2010-12-23 11:10:55 +0100 Wim Taymans + + * gst/rtpmanager/gstrtpbin.c: + docs: add some more gstrtpbin docs + +2010-12-22 21:27:11 +0100 Edward Hervey + + * sys/ximage/gstximagesrc.c: + ximagesrc: remote is a boolean (and not uint) property + +2010-12-22 19:58:21 +0100 Sebastian Dröge + + * gst/matroska/matroska-demux.c: + matroskademux: Don't use gst_pad_alloc_buffer() + Using this in a demuxer will cause deadlocks if there's + a pad with a pending pad-block downstream, no matter if + there is a queue between the pad or not. Queues pass + bufferalloc downstream from the same thread and only + act as a thread boundary for events and buffers. + +2010-12-22 14:14:08 +0000 Tim-Philipp Müller + + * gst/matroska/matroska-mux.c: + matroskamux: fix subtitle pad template, we only handle kate for now + +2010-12-16 11:44:44 +0000 Tim-Philipp Müller + + * gst/rtsp/gstrtspsrc.c: + docs: update rtspsrc docs, rtpbin is not in -bad any more + +2010-12-22 11:42:31 +0100 Wim Taymans + + * gst/rtpmanager/gstrtpsession.c: + rtpsession: unlock before emitting signals + +2010-12-21 22:34:49 +0100 Wim Taymans + + * gst/rtp/Makefile.am: + * gst/rtp/gstrtp.c: + * gst/rtp/gstrtpac3pay.c: + * gst/rtp/gstrtpac3pay.h: + rtpac3pay: add AC3 payloader + +2010-12-21 22:17:19 +0100 Wim Taymans + + * gst/rtp/gstrtpac3depay.c: + ac3depay: fix debug category description + +2010-12-21 22:16:42 +0100 Wim Taymans + + * gst/rtp/gstrtpmpapay.c: + mpapay: add debug category + +2010-12-20 14:49:02 -0300 Thiago Santos + + * tests/check/Makefile.am: + * tests/check/elements/jpegenc.c: + jpegenc: Adds another test case + Adds a test for jpegenc to check that is possible to negotiate and + push buffers with different resolution one after another. + https://bugzilla.gnome.org/show_bug.cgi?id=637686 + +2010-12-21 13:37:40 -0300 Thiago Santos + + * ext/jpeg/gstjpegenc.c: + jpegenc: sink pad's getcaps shouldn't use the src pad getcaps + Instead of using get_allowed_caps on the srcpad, the sinkpad getcaps + should use the getcaps of the srcpad's peer. This way the srcpad + can keep using fixed_caps and sinkpad getcaps exposes all caps + that can be negotiated + https://bugzilla.gnome.org/show_bug.cgi?id=637686 + +2010-12-21 16:58:47 +0100 Wim Taymans + + * gst/rtp/gstasteriskh263.c: + * gst/rtp/gstrtpL16depay.c: + * gst/rtp/gstrtpL16pay.c: + * gst/rtp/gstrtpac3depay.c: + * gst/rtp/gstrtpamrdepay.c: + * gst/rtp/gstrtpamrpay.c: + * gst/rtp/gstrtpbvdepay.c: + * gst/rtp/gstrtpbvpay.c: + * gst/rtp/gstrtpceltdepay.c: + * gst/rtp/gstrtpceltpay.c: + * gst/rtp/gstrtpdepay.c: + * gst/rtp/gstrtpdvdepay.c: + * gst/rtp/gstrtpdvpay.c: + * gst/rtp/gstrtpg722depay.c: + * gst/rtp/gstrtpg722pay.c: + * gst/rtp/gstrtpg723depay.c: + * gst/rtp/gstrtpg723pay.c: + * gst/rtp/gstrtpg726depay.c: + * gst/rtp/gstrtpg726pay.c: + * gst/rtp/gstrtpg729depay.c: + * gst/rtp/gstrtpg729pay.c: + * gst/rtp/gstrtpgsmdepay.c: + * gst/rtp/gstrtpgsmpay.c: + * gst/rtp/gstrtph263depay.c: + * gst/rtp/gstrtph263pay.c: + * gst/rtp/gstrtph263pdepay.c: + * gst/rtp/gstrtph263ppay.c: + * gst/rtp/gstrtph264depay.c: + * gst/rtp/gstrtph264pay.c: + * gst/rtp/gstrtpilbcdepay.c: + * gst/rtp/gstrtpilbcpay.c: + * gst/rtp/gstrtpj2kdepay.c: + * gst/rtp/gstrtpj2kpay.c: + * gst/rtp/gstrtpjpegdepay.c: + * gst/rtp/gstrtpjpegpay.c: + * gst/rtp/gstrtpmp1sdepay.c: + * gst/rtp/gstrtpmp2tdepay.c: + * gst/rtp/gstrtpmp2tpay.c: + * gst/rtp/gstrtpmp4adepay.c: + * gst/rtp/gstrtpmp4apay.c: + * gst/rtp/gstrtpmp4gdepay.c: + * gst/rtp/gstrtpmp4gpay.c: + * gst/rtp/gstrtpmp4vdepay.c: + * gst/rtp/gstrtpmp4vpay.c: + * gst/rtp/gstrtpmpadepay.c: + * gst/rtp/gstrtpmpapay.c: + * gst/rtp/gstrtpmparobustdepay.c: + * gst/rtp/gstrtpmpvdepay.c: + * gst/rtp/gstrtpmpvpay.c: + * gst/rtp/gstrtppcmadepay.c: + * gst/rtp/gstrtppcmapay.c: + * gst/rtp/gstrtppcmudepay.c: + * gst/rtp/gstrtppcmupay.c: + * gst/rtp/gstrtpqcelpdepay.c: + * gst/rtp/gstrtpqdmdepay.c: + * gst/rtp/gstrtpsirendepay.c: + * gst/rtp/gstrtpsirenpay.c: + * gst/rtp/gstrtpspeexdepay.c: + * gst/rtp/gstrtpspeexpay.c: + * gst/rtp/gstrtpsv3vdepay.c: + * gst/rtp/gstrtptheoradepay.c: + * gst/rtp/gstrtptheorapay.c: + * gst/rtp/gstrtpvorbisdepay.c: + * gst/rtp/gstrtpvorbispay.c: + * gst/rtp/gstrtpvrawdepay.c: + * gst/rtp/gstrtpvrawpay.c: + rtp: add RTP hint to the klass + +2010-12-21 16:49:28 +0100 Wim Taymans + + * gst/rtp/gstasteriskh263.c: + * gst/rtp/gstrtpL16depay.c: + * gst/rtp/gstrtpL16pay.c: + * gst/rtp/gstrtpac3depay.c: + * gst/rtp/gstrtpamrdepay.c: + * gst/rtp/gstrtpamrpay.c: + * gst/rtp/gstrtpbvdepay.c: + * gst/rtp/gstrtpbvpay.c: + * gst/rtp/gstrtpceltdepay.c: + * gst/rtp/gstrtpceltpay.c: + * gst/rtp/gstrtpdepay.c: + * gst/rtp/gstrtpdvdepay.c: + * gst/rtp/gstrtpdvpay.c: + * gst/rtp/gstrtpg722depay.c: + * gst/rtp/gstrtpg722pay.c: + * gst/rtp/gstrtpg723depay.c: + * gst/rtp/gstrtpg723pay.c: + * gst/rtp/gstrtpg726depay.c: + * gst/rtp/gstrtpg726pay.c: + * gst/rtp/gstrtpg729depay.c: + * gst/rtp/gstrtpg729pay.c: + * gst/rtp/gstrtpgsmdepay.c: + * gst/rtp/gstrtpgsmpay.c: + * gst/rtp/gstrtph263depay.c: + * gst/rtp/gstrtph263pay.c: + * gst/rtp/gstrtph263pdepay.c: + * gst/rtp/gstrtph263ppay.c: + * gst/rtp/gstrtph264depay.c: + * gst/rtp/gstrtph264pay.c: + * gst/rtp/gstrtpilbcdepay.c: + * gst/rtp/gstrtpilbcpay.c: + * gst/rtp/gstrtpj2kdepay.c: + * gst/rtp/gstrtpj2kpay.c: + * gst/rtp/gstrtpjpegdepay.c: + * gst/rtp/gstrtpjpegpay.c: + * gst/rtp/gstrtpmp1sdepay.c: + * gst/rtp/gstrtpmp2tdepay.c: + * gst/rtp/gstrtpmp2tpay.c: + * gst/rtp/gstrtpmp4adepay.c: + * gst/rtp/gstrtpmp4apay.c: + * gst/rtp/gstrtpmp4gdepay.c: + * gst/rtp/gstrtpmp4gpay.c: + * gst/rtp/gstrtpmp4vdepay.c: + * gst/rtp/gstrtpmp4vpay.c: + * gst/rtp/gstrtpmpadepay.c: + * gst/rtp/gstrtpmpapay.c: + * gst/rtp/gstrtpmparobustdepay.c: + * gst/rtp/gstrtpmpvdepay.c: + * gst/rtp/gstrtpmpvpay.c: + * gst/rtp/gstrtppcmadepay.c: + * gst/rtp/gstrtppcmapay.c: + * gst/rtp/gstrtppcmudepay.c: + * gst/rtp/gstrtppcmupay.c: + * gst/rtp/gstrtpqcelpdepay.c: + * gst/rtp/gstrtpqdmdepay.c: + * gst/rtp/gstrtpsirendepay.c: + * gst/rtp/gstrtpsirenpay.c: + * gst/rtp/gstrtpspeexdepay.c: + * gst/rtp/gstrtpspeexpay.c: + * gst/rtp/gstrtpsv3vdepay.c: + * gst/rtp/gstrtptheoradepay.c: + * gst/rtp/gstrtptheorapay.c: + * gst/rtp/gstrtpvorbisdepay.c: + * gst/rtp/gstrtpvorbispay.c: + * gst/rtp/gstrtpvrawdepay.c: + * gst/rtp/gstrtpvrawpay.c: + rtp: fix rank of payloaders and depayloaders + Set the payloaders and depayloaders to a reasonable rank. + +2010-12-21 15:24:18 +0100 Wim Taymans + + * gst/rtp/gstrtpvrawdepay.c: + vrawdepay: reset depayloader state + Reset the depayloader state on flush-stop. + +2010-12-21 15:07:14 +0100 Wim Taymans + + * gst/rtp/gstrtpmp4vpay.c: + * gst/rtp/gstrtpmp4vpay.h: + mp4pay: use vmethod for intercepting events + +2010-12-21 13:55:40 +0100 Wim Taymans + + * gst/rtp/gstrtptheorapay.c: + theorapay: clear packet on flush-stop + +2010-12-21 13:49:41 +0100 Wim Taymans + + * gst/rtp/gstrtpvorbispay.c: + vorbispay: clear packet on flush-stop + +2010-12-21 12:31:44 +0100 Wim Taymans + + * gst/rtp/gstrtpmp4gdepay.c: + mp4gdepay: reset depayloader state + +2010-12-21 12:29:58 +0100 Wim Taymans + + * gst/rtp/gstrtph264pay.c: + h264pay: flush adapter on flush-stop + +2010-12-20 18:49:49 +0100 Wim Taymans + + * gst/rtp/gstrtpmpapay.c: + mpapay: flush last packets on EOS + +2010-12-20 17:47:05 +0100 Edward Hervey + + * common: + Automatic update of common submodule + From 169462a to 46445ad + +2010-12-20 16:51:47 +0100 Wim Taymans + + * gst/rtp/gstrtpmpapay.c: + mpapay: reset payloader on state change + +2010-12-20 16:05:36 +0100 Wim Taymans + + * gst/rtp/gstrtpmpapay.c: + mpapay: reset payloader on flush + Reset the payloader on a flush event. + Handle DISCONT better. + +2010-12-20 15:54:45 +0100 Wim Taymans + + * gst/rtpmanager/rtpjitterbuffer.c: + jitterbuffer: get better buffering level + When the jitterbuffer contains -1 timestamps, make sure we still calculate the + buffer fill level by skipping the -1 buffers. + Try to be more resilient to weird input timestamps. + +2010-12-20 11:10:22 +0100 Wim Taymans + + * gst/rtpmanager/gstrtpjitterbuffer.c: + jitterbuffer: provide a clock. + since we are using the clock for sync, we need to also provide a clock for good + measure. The reason is that even if downstream elements provide a clock, we + don't want to have that clock selected because it might not be running yet. + +2010-12-20 10:49:56 +0100 Wim Taymans + + * gst/rtpmanager/gstrtpbin.c: + rtpbin: copy buffering stats + when we create an aggregate buffering message, copy the buffering stats form the + last message. At least we get correct buffering mode then. + +2010-12-19 11:02:41 +0100 Sebastian Dröge + + * tests/check/pipelines/wavenc.c: + wavenc: Fix memory leaks in the unit test + +2010-12-19 10:58:16 +0100 Sebastian Dröge + + * gst/effectv/gstradioac.c: + * gst/effectv/gstradioac.h: + radioactv: Prevent use of uninitialized values + Fixes bug #618652. + +2010-12-19 10:22:29 +0100 Sebastian Dröge + + * gst/debugutils/gstcapsdebug.c: + capsdebug: Don't leak pad templates created from static pad templates + +2010-11-29 12:36:06 +0000 Vincent Penquerc'h + + * sys/ximage/gstximagesrc.c: + * sys/ximage/gstximagesrc.h: + ximagesrc: change from XGetImage to XGetSubImage dependant on a property + ximagesrc: change from XGetImage to XGetSubImage dependant on a property + to avoid unnecessary performance hits by default. + +2010-11-28 16:04:35 +0000 Vincent Penquerc'h + + * sys/ximage/gstximagesrc.c: + ximagesrc: use XGetSubImage instead of XGetImage, works with remote X + ximagesrc: use XGetSubImage instead of XGetImage, works with remote X + (on my setup anyway...) + +2010-11-27 17:15:32 +0000 Vincent Penquerc'h + + * sys/ximage/gstximagesrc.c: + ximagesrc: fix various width/height calculations being off by one, + ximagesrc: fix various width/height calculations being off by one, + and make it so a single pixel width/height can be captured (except + the top left one, as 0,0,0,0 is reserved for full screen as per + the property comments). + +2010-12-17 19:19:35 -0600 Rob Clark + + * sys/v4l2/gstv4l2object.c: + fix compile errors on macosx + with i686-apple-darwin10-gcc-4.2.1: + gstv4l2object.c: In function 'gst_v4l2_object_get_nearest_size': + gstv4l2object.c:1988: warning: format '%u' expects type 'unsigned int', but argument 12 has type 'gint *' + gstv4l2object.c:1988: warning: format '%u' expects type 'unsigned int', but argument 13 has type 'gint *' + +2010-12-17 15:38:15 +0100 Mark Nauwelaerts + + * gst/rtp/gstrtph264depay.c: + rtph264depay: determine output h264 layout using caps negotiation + ... thereby (partially) deprecating properties currently controlling whether + or not byte-stream output or NAL/AU alignment (though properties still determine + fallback if nothing specified in caps). + Fixes #606662. + +2010-12-16 18:55:43 +0100 Wim Taymans + + * gst/rtp/gstrtpj2kpay.c: + j2kpay: handle EOC correctly + Don't include the next 2 bytes when we are at the end of the data and there are + no more bytes left. + +2010-12-16 15:15:49 +0100 Mark Nauwelaerts + + * ext/pulse/pulsesink.c: + pulsesink: flush remaining buffered samples on EOS + ... which can make a difference between all or nothing when dealing + with short streams and relatively large ringbuffer segment. + +2010-12-16 10:04:19 +0100 Sebastian Dröge + + * gst/deinterlace/gstdeinterlace.c: + deinterlace: Change classification to Filter/Effect/Video/Deinterlace + +2010-12-15 18:21:34 +0100 Edward Hervey + + * gst/rtp/gstrtpj2kpay.c: + rtpj2kpay: Initialize all fields + Makes sad compliers happy + +2010-12-15 16:22:54 +0100 Wim Taymans + + * gst/rtp/gstrtpj2kpay.c: + j2kpay: cleanup header construction + Use a simpler way of constructing the header that doesn't depend on + the endianness. + +2010-12-15 13:30:50 +0000 Tim-Philipp Müller + + * configure.ac: + configure: depend on -base from git for new rtp base depayloader features + This is ok in this case, since the plan is to release core/base again + along with good/ugly/bad in the next cycle. + +2010-12-15 14:55:58 +0200 Stefan Kost + + * common: + Automatic update of common submodule + From 20742ae to 169462a + +2010-12-15 13:12:09 +0100 Wim Taymans + + * gst/rtp/gstrtpj2kdepay.c: + * gst/rtp/gstrtpj2kdepay.h: + j2kdepay: add support for buffer lists + +2010-12-14 18:12:43 +0100 Wim Taymans + + * gst/rtpmanager/rtpsession.c: + session: fix average RTCP packet size some more. + Fix stupid error in averaging macro. + Include udp headers in packet length estimation. + +2010-12-14 17:15:23 +0100 Wim Taymans + + * gst/rtpmanager/rtpsession.c: + * gst/rtpmanager/rtpstats.c: + rtpbin: correctly calculate RTCP packet size + +2010-12-14 15:27:52 +0100 Wim Taymans + + * gst/rtp/gstrtpj2kpay.c: + j2kpay: stop scanning when we reached the end + Stop scanning for markers when we reached the end of the data. + +2010-12-13 16:23:24 +0200 Stefan Kost + + * common: + Automatic update of common submodule + From 011bcc8 to 20742ae + +2010-12-13 12:56:12 +0100 Wim Taymans + + * gst/rtpmanager/gstrtpjitterbuffer.c: + jitterbuffer: avoid leaking sink events + Avoid leaking the newsegment event when it has the wrong format. + +2010-12-12 14:53:17 +0100 Wim Taymans + + * gst/rtp/gstrtpmp4vpay.c: + mp4vpay: we can also accept xvid caps + +2010-12-12 01:39:06 +1100 Jan Schmidt + + * gst/deinterlace/gstdeinterlace.c: + deinterlace: Avoid infinite loop draining frames + When the pipeline is flushed just as we're draining history, + don't loop infinitely, just discard the history and abort. + +2010-12-11 17:39:20 +0000 Tim-Philipp Müller + + * ext/jpeg/gstjpegdec.c: + * ext/jpeg/gstjpegdec.h: + jpegdec: add "max-errors" property to ignore decoding errors + Add property to ignore decoding errors. Default is to ignore a few + decoding errors if the input is packetized, but error out immediately + if the input is not packetized. + Ignoring errors for packetized input most likely doesn't work + properly yet, so don't do that for now. + https://bugzilla.gnome.org/show_bug.cgi?id=623063 + +2010-05-28 15:27:14 +0100 Tim-Philipp Müller + + * ext/jpeg/gstjpegenc.c: + jpegenc: free/malloc instead of realloc, avoids memcpy + +2010-12-11 17:49:03 +0100 Sebastian Dröge + + * gst/qtdemux/qtdemux.c: + qtdemux: Check if there's actually a seek table before parsing it + +2010-12-11 17:46:17 +0100 Kishore Arepalli + + * gst/qtdemux/qtdemux.c: + qtdemux: Implement CONVERT and FORMATS query + Fixes bug #636784. + +2010-07-01 00:22:07 +0100 Tim-Philipp Müller + + * gst/matroska/matroska-demux.c: + matroska-demux: put unrecognised RIFF format IDs into the unknown caps + Extra info can't hurt. Field names aren't necessarily consistent with + what's used elsewhere though (e.g. avidemux), but then neither are the + caps. + https://bugzilla.gnome.org/show_bug.cgi?id=623178 + +2010-10-29 22:50:14 +0100 Jan Schmidt + + * ext/pulse/pulsemixerctrl.c: + * ext/pulse/pulsemixerctrl.h: + pulsemixer: Implement MIXER_FLAG_AUTO_NOTIFICATIONS + Add the mixer flag and send notifications when either the volume or muted + status changes. + https://bugzilla.gnome.org/show_bug.cgi?id=618389 + +2010-02-08 21:41:29 +0100 Mark Nauwelaerts + + * gst/rtsp/gstrtspsrc.c: + rtspsrc: mark DISCONT when resuming PLAY + In particular, when streaming interleaved, this arranges for setting a new + timestamp on outgoing buffer so downstream can appropriate reset + to a change in (rtp)time. + +2010-12-02 16:08:34 +0100 Mark Nauwelaerts + + * gst/rtsp/gstrtspsrc.c: + * gst/rtsp/gstrtspsrc.h: + rtspsrc: degrade gracefully upon failing seek and tweak QUERY_SEEKING response + +2010-10-25 11:51:06 +0200 Mark Nauwelaerts + + * gst/rtsp/gstrtspsrc.c: + rtspsrc: add and use auto buffering mode + ... which selects BUFFER for a non-live stream, and otherwise SLAVE. + Fixes #633088. + +2010-12-06 12:16:12 +0100 Wim Taymans + + * gst/rtp/gstrtpj2kdepay.c: + * gst/rtp/gstrtpj2kdepay.h: + j2kdepay: make the depayloader more resilient + Use 3 adapters, one to accumulate paketization units, another on to accumulate + tiles and a last one to accumulate the final frame. + Don't just blindly flush the adapter on DISCONT but only discard the current + packetization unit. + When we dropped jpeg2000 packets between SOP markers, adjust the SOT header with + the new lenght. + +2010-12-09 13:49:04 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + qtdemux: fix flow return aggregation + +2010-12-08 11:35:33 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + qtdemux: fix handling near end-of-file corner cases + Also, relax some error handling to not bail out completely when something + feels amiss, but consider this EOF and continue with was obtained so far. + +2010-12-07 17:19:00 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + qtdemux: fragmented support; fix offset handling and relax error raising + In particular, accept unknown stream in track fragment, and only error out + if that raises problems later on with respect to offset tracking. + Fixes #620283. + +2010-12-07 13:11:48 +0100 Mark Nauwelaerts + + * gst/flv/Makefile.am: + * gst/flv/gstflvdemux.c: + flvdemux: use aac codec-data to adjust samplerate if needed + Based on patch by Fabien Lebaillif-Delamare + Fixes #636621. + +2010-12-07 11:43:13 +0100 Wim Taymans + + * ext/pulse/pulsesink.c: + pulsesink: don't uncork in _start + Don't uncork in the _start method just yet but wait until we have written some + samples to pulseaudio. This avoid underruns on pulseaudio and less crackling + noises when starting. + +2010-12-06 19:59:49 +0100 Alessandro Decina + + * gst/qtdemux/qtdemux.c: + qtdemux: fix compiler warnings on OSX. + +2010-12-06 18:17:24 +0100 Mark Nauwelaerts + + * ext/jpeg/gstjpegdec.c: + jpegdec: add debug to notify when skipping to jpeg header + +2010-12-06 18:16:19 +0100 Mark Nauwelaerts + + * ext/jpeg/gstjpegdec.c: + jpegdec: discard incomplete image + ... as determined when finding SOI next image before an EOI. + Based on patch by David Hoyt + Fixes #635734. + +2010-12-06 17:45:38 +0100 Mark Nauwelaerts + + * ext/jpeg/gstjpegdec.c: + jpegdec: avoid infinite loop when resyncing + Fixes #635734 (partly). + +2010-12-06 15:21:53 +0100 David Hoyt + + * gst/imagefreeze/gstimagefreeze.c: + imagefreeze: pass along eos if received before buffer arrives + Fixes #636172. + +2010-10-20 11:05:49 +0200 Andoni Morales Alastruey + + * gst/matroska/ebml-write.c: + * gst/matroska/ebml-write.h: + * gst/matroska/matroska-mux.c: + matroskamux: try to write timestamps in all the outgoing buffers + Fixes #632654. + +2010-12-06 12:17:21 +0100 Mark Nauwelaerts + + * gst/debugutils/progressreport.c: + * gst/debugutils/progressreport.h: + progressreport: optionally determine progress using buffer metadata + Based on patch by Leo Singer + Fixes #629418. + +2010-12-05 14:39:19 +0100 Edward Hervey + + * tests/check/elements/interleave.c: + check: Fixup the shutting down order + First bring down everything to NULL before attempting to unlink + or unref anything. + Avoids the tests just hanging there for ever waiting to acquire a + lock that doesn't exist anymore. + +2010-11-04 19:31:45 +0100 Janne Grunau + + * sys/v4l2/gstv4l2bufferpool.c: + v4l2src: set top field first for interlaced buffers if v4l2 exports it + https://bugzilla.gnome.org/show_bug.cgi?id=634393 + +2010-11-04 18:36:09 +0100 Janne Grunau + + * sys/v4l2/gstv4l2object.c: + v4l2src: check field information and set interlaced caps accordingly + Reject the format if the field type is not supported. + https://bugzilla.gnome.org/show_bug.cgi?id=634391 + +2010-12-03 17:42:14 +0100 Benjamin Gaignard + + * Android.mk: + * android/NOTICE: + * android/apetag.mk: + * android/avi.mk: + * android/flv.mk: + * android/gst/rtpmanager/gstrtpbin-marshal.c: + * android/gst/rtpmanager/gstrtpbin-marshal.h: + * android/gst/udp/gstudp-enumtypes.c: + * android/gst/udp/gstudp-enumtypes.h: + * android/gst/udp/gstudp-marshal.c: + * android/gst/udp/gstudp-marshal.h: + * android/icydemux.mk: + * android/id3demux.mk: + * android/qtdemux.mk: + * android/rtp.mk: + * android/rtpmanager.mk: + * android/rtsp.mk: + * android/soup.mk: + * android/udp.mk: + * android/wavenc.mk: + * android/wavparse.mk: + Add build system for Android + +2010-03-26 13:51:58 +0100 Guillaume Emont + + * gst/debugutils/gstnavseek.c: + navseek: add basic support to change playback rate + The following keys will now be interpreted by navseek: + 'f' means fast forward: the stream gets played at rate 2.0 + 'r' means rewind: the stream gets played at rate -2.0 + 'n' means normal: the stream gets played at rate 1.0 + Fixes #631516. + +2010-12-01 13:12:04 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + qtdemux: add support for e(a)c-3 audio + +2010-11-19 12:44:35 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + qtdemux: avoid sending EOS event twice + +2010-11-19 12:44:18 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + qtdemux: remove dead code trying to update stream duration + On the one hand, it insufficiently checks whether it only updates a dummy + segment. On the other hand, only doing this at the time the last sampled is + prepared (and sent downstream) is too little too late. + +2010-11-09 10:58:57 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + qtdemux: fragmented support; handle ismv sample flags + +2010-11-08 11:41:21 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + qtdemux: fragmented support; handle ismv stbl atoms + ... or lack of some thereof, such as mandatory stsz. Shuffle some code + in _stbl_init to detect this early enough. + +2010-11-08 11:39:37 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + qtdemux: fragmented support; compensate for ismv offset handling + ... or lack thereof, which according to specs would put media data in + unlikely position. + +2010-11-04 14:07:56 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + * gst/qtdemux/qtdemux.h: + qtdemux: fragmented support for push mode + +2010-11-04 10:17:37 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + * gst/qtdemux/qtdemux.h: + qtdemux: fragmented support; proper and incremental moof parsing + That is, parse each moof in one pass (considering all contained streams' + metadata), and do so incrementally as needed for playback rather than + an initial complete scan of all moof (though all moov sample metadata + is fully parsed at startup). + +2010-11-04 10:06:30 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + qtdemux: refactor stream freeing + +2010-11-04 10:05:15 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + qtdemux: delegate linear search for sample to binary search when possible + Also arrange for parsing a sample prior to taking a reference to it, + which requires less memory layout assumptions for correctness. + +2010-11-01 15:52:29 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + qtdemux: fragmented support; handle moov samples and proper stream duration + +2010-11-01 13:40:05 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + qtdemux: fragmented support; consider mvex and handle flags and offset fields + +2010-10-28 16:49:41 +0200 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + qtdemux: fragmented support; forego check for short streams + ... as some bogus files may indicate streams of 0 duration in moov, + while indicating the complete movie duration in mvhd (the latter should + be in mehd). + +2010-10-28 16:46:48 +0200 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + * gst/qtdemux/qtdemux_types.h: + qtdemux: fragmented support; code cleanups and optimizations in atom parsing + Avoid extra allocation in _parse_trun, add more checks for parsing errors, + add or adjust some debug statement, fix comments, sprinkle some branch + prediction. + +2010-09-13 23:19:44 -0300 Thiago Santos + + * gst/qtdemux/qtdemux.c: + qtdemux: parse_moof should return TRUE on success + +2010-09-10 22:41:03 -0300 Thiago Santos + + * gst/qtdemux/qtdemux.c: + qtdemux: Fix iteration bug + Avoid infinite loop when iterating traf + +2010-09-10 21:32:26 -0300 Thiago Santos + + * gst/qtdemux/qtdemux.c: + qtdemux: Refactor trun parsing + The allocation of the samples can be placed out of the loop. + Makes the code clearer. + Also avoid relying on traf information as it is placed on the + end of the file and might not be acessible on push mode. + +2010-09-10 00:29:26 -0300 Thiago Santos + + * gst/qtdemux/qtdemux.c: + qtdemux: Remove parsing of unused atom + sdtp atom is parsed but not used, so we don't have to + parse it. + +2010-11-09 11:45:00 +0100 Mark Nauwelaerts + + * gst/qtdemux/qtdemux.c: + qtdemux: tweak wam support + ... with some comment and portability macros. + +2009-09-23 18:47:42 +0200 Marc-André Lureau + + * gst/qtdemux/qtdemux.c: + * gst/qtdemux/qtdemux_fourcc.h: + * gst/qtdemux/qtdemux_types.c: + qtdemux: support wma & vc-1 + https://bugzilla.gnome.org/show_bug.cgi?id=596321 + +2010-03-11 09:56:04 +0100 Andoni Morales Alastruey + + * gst/qtdemux/qtdemux.c: + * gst/qtdemux/qtdemux.h: + qtdemux: parse fmp4 samples information + The fragmented mp4 format stores the tracks and samples information in the + 'moof' boxes, which are appended before each fragment (fragment->'moof'+'mdat'). + The 'mfra' box stores the offset of each 'moof' box and their presentation + time. The location of this box can be retrieved from the 'mfro' box, which is + located at the end of the file. + The 'mfra' box is parsed to get the offset of each 'moof' box and their + presentation time. + Each 'moof' box can contain information for one or more tracks inside + 'tfhd' boxes. For each track in a 'moof', we have a 'trun' box, which + contains information of each sample (offset and duration) used to build + the samples table. + Based on patch by Marc-André Lureau + https://bugzilla.gnome.org/show_bug.cgi?id=596321 + +2010-03-11 15:34:49 +0100 Marc-André Lureau + + * gst/qtdemux/qtatomparser.h: + * gst/qtdemux/qtdemux_dump.c: + * gst/qtdemux/qtdemux_dump.h: + * gst/qtdemux/qtdemux_fourcc.h: + * gst/qtdemux/qtdemux_types.c: + * gst/qtdemux/qtdemux_types.h: + qtdemux: add fragmented mp4 fourccs + Adds fourcc's for tfra, tfhd, trun, sdtp, trex, mehd and + their dumps + https://bugzilla.gnome.org/show_bug.cgi?id=596321 + +2010-03-11 10:24:56 +0100 Marc-André Lureau + + * gst/qtdemux/qtdemux.c: + qtdemux: parse the track id from the track header + Signed-off-by: Andoni Morales Alastruey + https://bugzilla.gnome.org/show_bug.cgi?id=596321 + +2010-03-11 14:10:12 +0100 Marc-André Lureau + + * gst/qtdemux/qtdemux.c: + qtdemux: allow pulling atoms with unknown size + Signed-off-by: Andoni Morales Alastruey + https://bugzilla.gnome.org/show_bug.cgi?id=596321 + +2010-07-14 20:13:55 +0200 Marc-André Lureau + + * gst/qtdemux/qtdemux_dump.c: + qtdemux: make qtdemux_dump_mvhd parse version 1 correctly + Versions 0 and 1 of mvhd have different sizes of its values + (32bits/64bits). This patch makes it dump them correctly. + Also use the right node in the parameter and not the root node. + https://bugzilla.gnome.org/show_bug.cgi?id=596321 + +2010-11-19 12:45:00 +0100 Mark Nauwelaerts + + * gst/matroska/matroska-mux.c: + matroskademux: minor cleanups in setting streamheader on caps + +2010-11-02 17:04:04 +0100 Mark Nauwelaerts + + * gst/matroska/matroska-demux.c: + matroskademux: normalize empty Cues to no Cues + ... to trigger indexless seeking. + +2010-10-26 11:15:49 +0200 Mark Nauwelaerts + + * gst/avi/gstavidemux.c: + avidemux: add workaround for buggy list size + Fixes truncated extra-data in hdrl/strl/strf due to buggy containing + list size not accounting for padding in contained chunks. + +2010-12-02 16:11:01 +0100 Mark Nauwelaerts + + * gst/rtpmanager/gstrtpssrcdemux.c: + rtpssrcdemux: do not hold custom PAD_LOCK when pushing downstream + +2010-12-02 16:10:14 +0100 Mark Nauwelaerts + + * gst/rtsp/gstrtspsrc.c: + rtspsrc: reset session manager base time when flushing + ... as rtpbin uses running time to handle rtpjitterbuffer's buffer mode pauses. + +2010-12-01 16:51:33 +0100 Mark Nauwelaerts + + * gst/rtsp/gstrtspsrc.c: + rtspsrc: include range request for all streams with non-aggregate control + +2010-10-07 14:50:53 +0200 Mark Nauwelaerts + + * gst/rtsp/gstrtspsrc.c: + rtspsrc: fix debug statement + +2010-12-03 15:38:00 +0100 Edward Hervey + + * gst/avi/gstavidemux.c: + avidemux: Parse more variants of numerical IDIT tag + +2010-05-07 17:30:30 +0200 Edward Hervey + + * ext/libpng/gstpngenc.c: + pngenc: Use proper framerate range in caps + +2010-12-03 15:04:26 +0100 Edward Hervey + + * tests/check/pipelines/wavenc.c: + tests: Fix previously unbuildable/untested wavenc test + +2010-10-24 15:21:08 +0200 Edward Hervey + + * gst/flv/gstflvdemux.c: + flvdemux: Refactor tag pushing logic + The logic of when to push was wrong also (resulting in some tags never + being pushed). + +2010-10-24 15:20:27 +0200 Edward Hervey + + * gst/flv/Makefile.am: + * gst/flv/gstflvdemux.c: + flvdemux: Use pbutils for codec descriptions + +2010-04-13 11:29:30 +0200 Edward Hervey + + * tests/check/elements/udpsink.c: + check: Use fail_unless_equals_int instead of fail_if + Makes the error message more interesting + +2010-11-30 19:22:11 +0100 Edward Hervey + + * gst/avi/gstavidemux.c: + avidemux: Also extract IDIT tags present too early + https://bugzilla.gnome.org/show_bug.cgi?id=636143 + +2010-11-30 19:21:23 +0100 Edward Hervey + + * gst/avi/gstavidemux.c: + avidemux: Also emit DateTime tag + https://bugzilla.gnome.org/show_bug.cgi?id=636143 + +2010-12-03 00:22:48 +0000 Tim-Philipp Müller + + * gst/wavparse/gstwavparse.c: + wavparse: detect DTS advertised as PCM correctly in some more cases + The DTS typefinder may return a lower probability for frames that start + at non-zero offsets and where there's no second frame sync in the first + buffer. It's fairly unlikely that we'll acidentally identify PCM data + as DTS, so we don't do additional checks for now. + https://bugzilla.gnome.org/show_bug.cgi?id=636234 + +2010-11-08 17:11:42 +0200 Stefan Kost + + * tests/check/Makefile.am: + tests: makefile cleanup + Fix indentation. Use $(GST_MAJORMINOR) instead of hardcoded 0.10. + +2010-11-08 17:02:56 +0200 Stefan Kost + + * tests/check/Makefile.am: + * tests/check/pipelines/.gitignore: + * tests/check/pipelines/wavenc.c: + tests: add a test for wav muxing + +2010-11-08 16:57:17 +0200 Stefan Kost + + * tests/check/elements/interleave.c: + * tests/check/pipelines/wavpack.c: + tests: remove newlines between variable decls (old gst-indent failure) + +2010-11-08 14:47:04 +0200 Stefan Kost + + * ext/libpng/gstpngdec.c: + pngdec: use png_error() as recommended by libpng docs to signal an error + Without that the element loops endlessly on broekn pngs. Fixes #634314 + +2010-11-16 17:48:16 -0300 Thiago Santos + + * gst/qtdemux/qtdemux.c: + qtdemux: Parse and use creation time tag from mvhd + Expose creation time from mvhd as a datetime tag + Fixes #634928 + +2010-10-27 19:15:20 +0200 Andoni Morales Alastruey + + * gst/icydemux/gsticydemux.c: + icydemux: Add 'StreamUrl' metadata as GST_TAG_HOMEPAGE tag + +2010-10-23 19:34:00 -0400 Tom Janiszewski + + * gst/flv/gstflvmux.c: + flvmux: Fix for nellymoser codecid setting + Fixes bug #632897. + +2010-10-21 16:15:08 +0200 Sebastian Dröge + + * gst/matroska/matroska-mux.c: + matroskamux: Add support for E-AC3 + +2010-10-21 16:14:44 +0200 Sebastian Dröge + + * gst/matroska/matroska-mux.c: + matroskamux: Add support for DTS + +2010-10-31 18:08:17 +0100 Sebastian Dröge + + * ext/soup/gstsouphttpsrc.c: + souphttpsrc: Don't send seeks behind the end of file to the server + Also improve debug output, re-initialize the content size and let the + seek handler error out on invalid seek segments. + Fixes bug #632977. + +2010-12-02 17:53:42 +0100 Wim Taymans + + * gst/rtp/gstrtpj2kpay.c: + j2kpay: use SOP markers to split bitstream + When parsing the bitstream, look for SOP markers because we are allowed to split + packets on those marker boundaries. + Rework the parsing code a little so that we can pack multiple Packetization + units in one RTP packet. + +2010-11-18 12:49:47 +0100 Wim Taymans + + * gst/rtp/gstrtpj2kpay.c: + * gst/rtp/gstrtpj2kpay.h: + rtpj2kpay: use buffer lists + Use buffer lists for doing zerocopy payloading. + Add property to disable buffer lists. + +2010-11-16 16:54:25 +0100 Wim Taymans + + * gst/rtp/gstrtph264pay.c: + h264pay: small cleanups + Allocate adapter only once. + Make some guint8 * const. + +2010-11-16 15:39:24 +0100 Tambet Ingo + + * gst/rtp/gstrtph264pay.c: + * gst/rtp/gstrtph264pay.h: + rtph264pay: implement full bytestream scan mode. + Implement the full bytestream scan mode. + Fixes #634910 + +2010-11-15 10:52:31 +0100 Wim Taymans + + * tests/examples/rtp/client-H263p-AMR.sh: + * tests/examples/rtp/client-H263p-PCMA.sh: + * tests/examples/rtp/client-H263p.sh: + * tests/examples/rtp/client-H264-PCMA.sh: + * tests/examples/rtp/client-H264.sh: + * tests/examples/rtp/client-PCMA.sh: + * tests/examples/rtp/server-v4l2-H264-alsasrc-PCMA.sh: + examples: improve RTP examples + Make the examples use autovideosink and ffmpegcolorspace for better + compàtibility. + Make some more variables for the sink and the decoders. + Set zerolatency tuning on x264enc for better realtime results. + +2010-11-10 11:04:48 +0100 Wim Taymans + + * gst/rtsp/gstrtspsrc.c: + * gst/rtsp/gstrtspsrc.h: + rtspsrc: select multicast transports in a smarter way + When we see a multicast address in the SDP connection, only try to negotiate a + multicast transport with the server. + Fixes #634093 + +2010-12-02 18:14:16 +0000 Tim-Philipp Müller + + * configure.ac: + Bump GLib requirement to implicit requirement + ie. >= 2.20 while we depend on core/base 0.10.31 + +2010-12-02 18:13:57 +0000 Tim-Philipp Müller + + * configure.ac: + * docs/plugins/gst-plugins-good-plugins.hierarchy: + * docs/plugins/inspect/plugin-1394.xml: + * docs/plugins/inspect/plugin-aasink.xml: + * docs/plugins/inspect/plugin-alaw.xml: + * docs/plugins/inspect/plugin-alpha.xml: + * docs/plugins/inspect/plugin-alphacolor.xml: + * docs/plugins/inspect/plugin-annodex.xml: + * docs/plugins/inspect/plugin-apetag.xml: + * docs/plugins/inspect/plugin-audiofx.xml: + * docs/plugins/inspect/plugin-auparse.xml: + * docs/plugins/inspect/plugin-autodetect.xml: + * docs/plugins/inspect/plugin-avi.xml: + * docs/plugins/inspect/plugin-cacasink.xml: + * docs/plugins/inspect/plugin-cairo.xml: + * docs/plugins/inspect/plugin-cutter.xml: + * docs/plugins/inspect/plugin-debug.xml: + * docs/plugins/inspect/plugin-deinterlace.xml: + * docs/plugins/inspect/plugin-dv.xml: + * docs/plugins/inspect/plugin-efence.xml: + * docs/plugins/inspect/plugin-effectv.xml: + * docs/plugins/inspect/plugin-equalizer.xml: + * docs/plugins/inspect/plugin-esdsink.xml: + * docs/plugins/inspect/plugin-flac.xml: + * docs/plugins/inspect/plugin-flv.xml: + * docs/plugins/inspect/plugin-flxdec.xml: + * docs/plugins/inspect/plugin-gconfelements.xml: + * docs/plugins/inspect/plugin-gdkpixbuf.xml: + * docs/plugins/inspect/plugin-goom.xml: + * docs/plugins/inspect/plugin-goom2k1.xml: + * docs/plugins/inspect/plugin-gstrtpmanager.xml: + * docs/plugins/inspect/plugin-halelements.xml: + * docs/plugins/inspect/plugin-icydemux.xml: + * docs/plugins/inspect/plugin-id3demux.xml: + * docs/plugins/inspect/plugin-imagefreeze.xml: + * docs/plugins/inspect/plugin-interleave.xml: + * docs/plugins/inspect/plugin-jpeg.xml: + * docs/plugins/inspect/plugin-level.xml: + * docs/plugins/inspect/plugin-matroska.xml: + * docs/plugins/inspect/plugin-mulaw.xml: + * docs/plugins/inspect/plugin-multifile.xml: + * docs/plugins/inspect/plugin-multipart.xml: + * docs/plugins/inspect/plugin-navigationtest.xml: + * docs/plugins/inspect/plugin-oss4.xml: + * docs/plugins/inspect/plugin-ossaudio.xml: + * docs/plugins/inspect/plugin-png.xml: + * docs/plugins/inspect/plugin-pulseaudio.xml: + * docs/plugins/inspect/plugin-quicktime.xml: + * docs/plugins/inspect/plugin-replaygain.xml: + * docs/plugins/inspect/plugin-rtp.xml: + * docs/plugins/inspect/plugin-rtsp.xml: + * docs/plugins/inspect/plugin-shapewipe.xml: + * docs/plugins/inspect/plugin-shout2send.xml: + * docs/plugins/inspect/plugin-smpte.xml: + * docs/plugins/inspect/plugin-soup.xml: + * docs/plugins/inspect/plugin-spectrum.xml: + * docs/plugins/inspect/plugin-speex.xml: + * docs/plugins/inspect/plugin-taglib.xml: + * docs/plugins/inspect/plugin-udp.xml: + * docs/plugins/inspect/plugin-video4linux2.xml: + * docs/plugins/inspect/plugin-videobox.xml: + * docs/plugins/inspect/plugin-videocrop.xml: + * docs/plugins/inspect/plugin-videofilter.xml: + * docs/plugins/inspect/plugin-videomixer.xml: + * docs/plugins/inspect/plugin-wavenc.xml: + * docs/plugins/inspect/plugin-wavpack.xml: + * docs/plugins/inspect/plugin-wavparse.xml: + * docs/plugins/inspect/plugin-ximagesrc.xml: + * docs/plugins/inspect/plugin-y4menc.xml: + * win32/common/config.h: + Back to development + +=== release 0.10.26 === + +2010-12-01 21:15:09 +0000 Tim-Philipp Müller + + * ChangeLog: + * NEWS: + * RELEASE: + * configure.ac: + * docs/plugins/gst-plugins-good-plugins.args: + * docs/plugins/gst-plugins-good-plugins.hierarchy: + * docs/plugins/gst-plugins-good-plugins.interfaces: + * docs/plugins/gst-plugins-good-plugins.prerequisites: + * docs/plugins/inspect/plugin-1394.xml: + * docs/plugins/inspect/plugin-aasink.xml: + * docs/plugins/inspect/plugin-alaw.xml: + * docs/plugins/inspect/plugin-alpha.xml: + * docs/plugins/inspect/plugin-alphacolor.xml: + * docs/plugins/inspect/plugin-annodex.xml: + * docs/plugins/inspect/plugin-apetag.xml: + * docs/plugins/inspect/plugin-audiofx.xml: + * docs/plugins/inspect/plugin-auparse.xml: + * docs/plugins/inspect/plugin-autodetect.xml: + * docs/plugins/inspect/plugin-avi.xml: + * docs/plugins/inspect/plugin-cacasink.xml: + * docs/plugins/inspect/plugin-cairo.xml: + * docs/plugins/inspect/plugin-cutter.xml: + * docs/plugins/inspect/plugin-debug.xml: + * docs/plugins/inspect/plugin-deinterlace.xml: + * docs/plugins/inspect/plugin-dv.xml: + * docs/plugins/inspect/plugin-efence.xml: + * docs/plugins/inspect/plugin-effectv.xml: + * docs/plugins/inspect/plugin-equalizer.xml: + * docs/plugins/inspect/plugin-esdsink.xml: + * docs/plugins/inspect/plugin-flac.xml: + * docs/plugins/inspect/plugin-flv.xml: + * docs/plugins/inspect/plugin-flxdec.xml: + * docs/plugins/inspect/plugin-gconfelements.xml: + * docs/plugins/inspect/plugin-gdkpixbuf.xml: + * docs/plugins/inspect/plugin-goom.xml: + * docs/plugins/inspect/plugin-goom2k1.xml: + * docs/plugins/inspect/plugin-gstrtpmanager.xml: + * docs/plugins/inspect/plugin-halelements.xml: + * docs/plugins/inspect/plugin-icydemux.xml: + * docs/plugins/inspect/plugin-id3demux.xml: + * docs/plugins/inspect/plugin-imagefreeze.xml: + * docs/plugins/inspect/plugin-interleave.xml: + * docs/plugins/inspect/plugin-jpeg.xml: + * docs/plugins/inspect/plugin-level.xml: + * docs/plugins/inspect/plugin-matroska.xml: + * docs/plugins/inspect/plugin-mulaw.xml: + * docs/plugins/inspect/plugin-multifile.xml: + * docs/plugins/inspect/plugin-multipart.xml: + * docs/plugins/inspect/plugin-navigationtest.xml: + * docs/plugins/inspect/plugin-oss4.xml: + * docs/plugins/inspect/plugin-ossaudio.xml: + * docs/plugins/inspect/plugin-png.xml: + * docs/plugins/inspect/plugin-pulseaudio.xml: + * docs/plugins/inspect/plugin-quicktime.xml: + * docs/plugins/inspect/plugin-replaygain.xml: + * docs/plugins/inspect/plugin-rtp.xml: + * docs/plugins/inspect/plugin-rtsp.xml: + * docs/plugins/inspect/plugin-shapewipe.xml: + * docs/plugins/inspect/plugin-shout2send.xml: + * docs/plugins/inspect/plugin-smpte.xml: + * docs/plugins/inspect/plugin-soup.xml: + * docs/plugins/inspect/plugin-spectrum.xml: + * docs/plugins/inspect/plugin-speex.xml: + * docs/plugins/inspect/plugin-taglib.xml: + * docs/plugins/inspect/plugin-udp.xml: + * docs/plugins/inspect/plugin-video4linux2.xml: + * docs/plugins/inspect/plugin-videobox.xml: + * docs/plugins/inspect/plugin-videocrop.xml: + * docs/plugins/inspect/plugin-videofilter.xml: + * docs/plugins/inspect/plugin-videomixer.xml: + * docs/plugins/inspect/plugin-wavenc.xml: + * docs/plugins/inspect/plugin-wavpack.xml: + * docs/plugins/inspect/plugin-wavparse.xml: + * docs/plugins/inspect/plugin-ximagesrc.xml: + * docs/plugins/inspect/plugin-y4menc.xml: + * gst-plugins-good.doap: + * win32/common/config.h: + Release 0.10.26 2010-11-30 15:28:50 -0800 David Schleef diff --git a/NEWS b/NEWS index 7c3431d968..5eb6f34475 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,107 @@ -This is GStreamer Good Plug-ins 0.10.26, "Escapades" +This is GStreamer Good Plug-ins 0.10.27, "Some Kind of Temporal Blend" + +Changes since 0.10.26: + + * avidemux: add workaround for buggy list size; extract datetime tags + * cacasink: fix masks and strides + * deinterlace: change the default to linear + * deinterlace: avoid infinite loop draining + * deinterlace: rewrite/fix how neighboring scan lines are calculated + * flvdemux: use aac codec-data to adjust samplerate if needed + * flvmux: Fix for nellymoser codecid setting + * icydemux: Add 'StreamUrl' metadata as GST_TAG_HOMEPAGE tag + * id3demux: fix parsing of ID3v2.4 genre frames with multiple genres + * imagefreeze: pass along eos if received before buffer arrives + * jpegdec: add "max-errors" property to ignore decoding errors + * jpegdec: avoid infinite loop when resyncing; discard incomplete image + * matroskademux: add stream-format and alignment properties for h264 + * matroskademux: assume matroska if no doctype is specified + * matroskademux: increase allowed max. block size for push mode from 10M to 15M + * matroskademux: normalize empty Cues to no Cues + * matroskamux: add support for DTS and E-AC3 audio + * matroskamux: try to write timestamps in all the outgoing buffers + * multifilesink: send stream headers in key-frame mode + * multiudpsink: add buffer-size property + * navseek: add basic support to change playback rate + * pulsemixer: Implement MIXER_FLAG_AUTO_NOTIFICATIONS + * pulsesink: flush remaining buffered samples on EOS + * pulsesink: make corking during pause synchronous; don't uncork in _start + * pulsesink: Uncork stream while flushing the ringbuffer + * pulsesrc: add "client" property + * qtdemux: add support for fragmented mp4 + * qtdemux: add support for (E)AC-3, WMA and VC-1 audio + * qtdemux: allow pulling atoms with unknown size + * qtdemux: fix flow return aggregation and handling of near end-of-file corner cases + * qtdemux: parse and use creation time tag from mvhd + * rtpbin: copy buffering stats + * rtpbin: correctly calculate RTCP packet size + * rtp: fix rank of payloaders and depayloaders + * rtp: flush state on flush-stop for seek handling for many (de)payloaders + * rtp ac3pay: add AC3 payloader + * rtp h264depay: determine output h264 layout using caps negotiation + * rtp h264pay: implement full bytestream scan mode + * rtp j2kdepay: add support for buffer lists; make depayloader more resilient + * rtp j2kpay: use buffer lists for better performance + * rtp j2kpay: handle EOC correctly; stop scanning when we reached the end + * rtp j2kpay: use SOP markers to split bitstream + * rtp jitterbuffer: provide a clock; get better buffering level + * rtp jpegdepay: fix framerate parsing for locales that use a comma as floating point + * rtp mp4adepay: improve timestamps on outgoing packets + * rtpsession: also emit RTCP activity on SR + * rtpsession: remember last sent RB values + * rtspsrc: add and use auto buffering mode + * rtspsrc: degrade gracefully upon failing seek and tweak QUERY_SEEKING response + * rtspsrc: include range request for all streams with non-aggregate control + * rtspsrc: increase udp buffer size + * rtspsrc: reset session manager base time when flushing + * rtspsrc: select multicast transports in a smarter way + * souphttpsrc: don't send seeks behind the end of file to the server + * v4l2sink: add navigation support; properties to control crop + * vrawdepay: fix length check + * wavparse: detect DTS advertised as PCM correctly in some more cases + * ximagesrc: change from XGetImage to XGetSubImage dependant on a property + +Bugs fixed since 0.10.26: + + * 596321 : qtdemux: add support for fragmented MP4 and " mfra " boxes + * 618389 : [pulsemixer] Should implement MIXER_FLAG_AUTO_NOTIFICATIONS interface + * 618652 : [effectv] Use of uninitialised value in unit test + * 620283 : Support for Adobe's F4F missing + * 621929 : [PLUGIN-MOVE] move jack plugin from -bad to -good + * 623178 : [matroskademux] error message for unrecognised FOURCC codes should be improved + * 625825 : cannot link rtpmp4adepay ! aacparse + * 629418 : progressreport: add support for determining stream position from buffer timestamps instead of using queries + * 631516 : [navseek] Add support to change playback rate + * 632654 : [matroskamux] try to write timestamps in most of the outgoing buffers + * 632897 : flvmux does not set the correct nellymoser codec id + * 633280 : [icydemux][PATCH] icydemux: Send 'StreamUrl' metadata as GST_TAG_HOMEPAGE tag + * 634314 : pngdec hangs on faulty pngs + * 634391 : [v4l2src] add interlaced field to caps + * 634393 : v4l2src: Set top field first for interlaced captures + * 634910 : [rtph264pay] Implement bytestream scan mode + * 634928 : [qtdemux] report creation/modification time via metadata tag + * 635734 : jpegdec: infinite loop when playing back motion jpeg stream + * 636049 : ximagesrc: fix remote X and off by ones + * 636172 : imagefreeze: eos is not passed before a buffer arrives + * 636234 : [wavparse] dts 6ch played as stereo 16 bit pcm if DTS frame starts at non-zero offset + * 636621 : flvdemux: doesn't set the right sample rate for aac audio + * 636784 : [qtdemux] GST_QUERY_CONVERT implementation for qtdemux + * 637060 : matroskademux: errors out on 13MB blocks when streaming + * 637686 : [jpegenc] Improve sinkpad getcaps results + * 638019 : [matroskademux] some matroska files are not specifying DocType + * 638072 : build failure: rtpsource.c: error: 'have_rb' may be used uninitialized in this function + * 638535 : id3demux: multiple genres as per ID3v2.4 not supported correctly + * 638569 : cacasink crashes when given 15-bit video. + * 639240 : pulsesink: PLAYING- > PAUSED- > PLAYING transition causes dropout + * 639321 : deinterlace: field{1,3} scanline pointers seem to be off by one field line + * 639339 : v4l2: fails to build with older kernels due to missing V4L_FIELD_INTERLACED_{TB,BT} + * 639516 : muxers: fix setting src pad caps + * 639740 : [pulsesink] doesn't uncork in some cases during reverse playback + * 640028 : [qtdemux] crash on malformed mov stream + * 640063 : rtph264depay: leaks codec data buffer in byte-stream=false mode + * 640064 : rtspsrc memory leak + * 640080 : rtspsrc: fails to error out properly on network failure + * 623063 : [jpegdec] add " max-errors " property Changes since 0.10.25: diff --git a/RELEASE b/RELEASE index 7ed324d26c..83a14b184e 100644 --- a/RELEASE +++ b/RELEASE @@ -1,5 +1,5 @@ -Release notes for GStreamer Good Plug-ins 0.10.26 "Escapades" +Release notes for GStreamer Good Plug-ins 0.10.27 "Some Kind of Temporal Blend" @@ -9,8 +9,6 @@ GStreamer Good Plug-ins. The 0.10.x series is a stable series targeted at end users. -It is not API or ABI compatible with the stable 0.8.x series. -It is, however, parallel installable with the 0.8.x series. @@ -54,125 +52,106 @@ contains a set of less supported plug-ins that haven't passed the Features of this release - * alphacolor: make passthrough work - * avidemux: reverse playback fixes; prevent overlap of subsequent fragments - * deinterlace: remove assembly code in favor of orc - * dvdemux: parse SMPTE time codes - * flvdemux: parse and use cts (fixes jittery H.264 playback in some cases) - * flvmux: resend onMetada tag when tags changes in streamable mode - * g729pay: extend from right parent - * gconf: Don't install schemas when GConf is disabled - * goom, goom2k1: add latency compensation code, report latency correctly - * gstrtpjpegpay: Added Define Restart Interval (DRI) Marker - * h264depay: always mark the codec_data as keyframe - * icydemux: forward tag events - * id3v2mux: Add mapping for album artist - * imagefreeze: generate a perfectly timestamped stream - * level: avoid division by zero on silence - * matroskademux: more robustness for parse errors and corner-cases - * matroskademux: extract H.264 profile and level and set on caps - * matroskamux: reduce newsegment event spam and set discont flag where needed - * pulse: allow setting of pulse stream properties - * pulse: fix device_description in READY - * pulsesink: Add "client" property to set the PA client name - * pulsesink: share the PA context between all clients with the same name - * qtdemux: export AAC/MPEG-4/H.264 profile and level in caps - * rtp: add G722 payloader and depayloader elements - * rtpamr(de)pay: support AMR-WB SID frame - * rtpamrpay: proper duration for multiple frame payload; properly support perfect-rtptime - * rtpbin: add "ntp-sync" property and "use-pipeline-clock" properties - * rtpg729pay: properly support perfect-rtptime - * rtph264depay: only set delta unit on all-non-key units - * rtpmanager: provide additional statistics - * rtpmp4adepay: grab the sampling rate and put into caps - * rtpmparobustdepay: properly insert dummy buffers; use valid bitrate for dummy frame - * rtpmpvpay: fix timestamping of rtp buffers - * rtpsession: Add the option to auto-discover the RTP bandwidth - * rtpsession: Calculate RTCP bandwidth as a fraction of the RTP bandwidth - * rtpsession: Count sent RTCP packets after they have been finished - * rtpsession: relax third-party collision detection - * rtpstats: Rectify description of current_time in RTPArrivalStats - * rtspext: stop configuration on first failure - * rtspsrc: Add property to configure udpsrc buffer size - * rtspsrc: add rtsp-sdp protocol support - * rtspsrc: don't add /UDP in the transport, it's the default - * rtspsrc: fix duration reporting - * rtspsrc: handle stale digest authentication session data - * rtspsrc: use sdp uri parse method - * shapewipe: add optional border parameter and slowdown animation - * shapewipe: Force format to AYUV in the example pipeline for the same reason - * shapewipe: Force the input to AYUV to prevent negotiation failures in videomixer - * spectrum: only aggregate magnitude/phase if user asks for it, performance fixes - * v4l2src: add controllable colorbalance parameters, add decimate property - * v4l2src: fix using mpegts via the mmap interface; use GstBaseSrc::block-size as fallback size - * videomixer2: new videomixer2 element that behaves better than videomixer - * vrawdepay: handle invalid payload better + * avidemux: add workaround for buggy list size; extract datetime tags + * cacasink: fix masks and strides + * deinterlace: change the default to linear + * deinterlace: avoid infinite loop draining + * deinterlace: rewrite/fix how neighboring scan lines are calculated + * flvdemux: use aac codec-data to adjust samplerate if needed + * flvmux: Fix for nellymoser codecid setting + * icydemux: Add 'StreamUrl' metadata as GST_TAG_HOMEPAGE tag + * id3demux: fix parsing of ID3v2.4 genre frames with multiple genres + * imagefreeze: pass along eos if received before buffer arrives + * jpegdec: add "max-errors" property to ignore decoding errors + * jpegdec: avoid infinite loop when resyncing; discard incomplete image + * matroskademux: add stream-format and alignment properties for h264 + * matroskademux: assume matroska if no doctype is specified + * matroskademux: increase allowed max. block size for push mode from 10M to 15M + * matroskademux: normalize empty Cues to no Cues + * matroskamux: add support for DTS and E-AC3 audio + * matroskamux: try to write timestamps in all the outgoing buffers + * multifilesink: send stream headers in key-frame mode + * multiudpsink: add buffer-size property + * navseek: add basic support to change playback rate + * pulsemixer: Implement MIXER_FLAG_AUTO_NOTIFICATIONS + * pulsesink: flush remaining buffered samples on EOS + * pulsesink: make corking during pause synchronous; don't uncork in _start + * pulsesink: Uncork stream while flushing the ringbuffer + * pulsesrc: add "client" property + * qtdemux: add support for fragmented mp4 + * qtdemux: add support for (E)AC-3, WMA and VC-1 audio + * qtdemux: allow pulling atoms with unknown size + * qtdemux: fix flow return aggregation and handling of near end-of-file corner cases + * qtdemux: parse and use creation time tag from mvhd + * rtpbin: copy buffering stats + * rtpbin: correctly calculate RTCP packet size + * rtp: fix rank of payloaders and depayloaders + * rtp: flush state on flush-stop for seek handling for many (de)payloaders + * rtp ac3pay: add AC3 payloader + * rtp h264depay: determine output h264 layout using caps negotiation + * rtp h264pay: implement full bytestream scan mode + * rtp j2kdepay: add support for buffer lists; make depayloader more resilient + * rtp j2kpay: use buffer lists for better performance + * rtp j2kpay: handle EOC correctly; stop scanning when we reached the end + * rtp j2kpay: use SOP markers to split bitstream + * rtp jitterbuffer: provide a clock; get better buffering level + * rtp jpegdepay: fix framerate parsing for locales that use a comma as floating point + * rtp mp4adepay: improve timestamps on outgoing packets + * rtpsession: also emit RTCP activity on SR + * rtpsession: remember last sent RB values + * rtspsrc: add and use auto buffering mode + * rtspsrc: degrade gracefully upon failing seek and tweak QUERY_SEEKING response + * rtspsrc: include range request for all streams with non-aggregate control + * rtspsrc: increase udp buffer size + * rtspsrc: reset session manager base time when flushing + * rtspsrc: select multicast transports in a smarter way + * souphttpsrc: don't send seeks behind the end of file to the server + * v4l2sink: add navigation support; properties to control crop + * vrawdepay: fix length check + * wavparse: detect DTS advertised as PCM correctly in some more cases + * ximagesrc: change from XGetImage to XGetSubImage dependant on a property Bugs fixed in this release + * 596321 : qtdemux: add support for fragmented MP4 and " mfra " boxes + * 618389 : [pulsemixer] Should implement MIXER_FLAG_AUTO_NOTIFICATIONS interface + * 618652 : [effectv] Use of uninitialised value in unit test + * 620283 : Support for Adobe's F4F missing + * 621929 : [PLUGIN-MOVE] move jack plugin from -bad to -good + * 623178 : [matroskademux] error message for unrecognised FOURCC codes should be improved * 625825 : cannot link rtpmp4adepay ! aacparse - * 629047 : segfault in seek matroskademux - * 537544 : [pulse] allow setting pa context properties - * 628996 : pulsesink broken after shared context patch (bug #624338) - * 529672 : Big latency and bad framerate while mixing multiple live streams - * 581294 : rtspext: extensions configure_stream methods conflict - * 598915 : qtdemux: propagate jpeg2000 header data in image/x-j2c - * 612313 : qtdemux: Post AAC profile/level in caps - * 616521 : qtdemux: Export MPEG-4 video profile and level in stream caps - * 617318 : matroskademux, qtdemux: Use pbutils for H.264 profile/level extraction - * 620790 : [matroskademux] general stream error when trying to play certain .mkv file - * 622390 : [v4l2] add controllable color balance properties / programmable camera - * 624338 : [pulsesink] Handle pulse context separate from the ringbuffers and share them - * 625547 : imagefreeze unit test fails occasionally - * 626048 : [videomixer] needs mode that syncs streams based on timestamps - * 626518 : [imagefreeze] better caps negotiation - * 627162 : [pulse] better fallback return value for gst_pulse_client_name() - * 627174 : [pulsesink] new property to tune the PA client name - * 627289 : souphttpsrc: tweak error messages - * 627341 : wavparse: strange handling of files less than 12 bytes - * 627796 : rtpbin: add ntp clock sync - * 628020 : [pulsesink] assertion failure in change_state NULL- > READY - * 628058 : Need a way to set the SO_RCVBUF property on rtsp-based sockets. - * 628127 : jpeg rtp payloader crashes when there is corruption in the jpeg byte stream. - * 628214 : Add support to RTSP initiation through SDP files - * 628349 : [v4l2src] Doesn't support capturing mpegts using mmap - * 628454 : Matroska demuxer doesn't handle DATE tag if it contains only a year number - * 628608 : [alphacolor] element classification is wrong - * 629018 : rtpjpegpay: unable to build because of uninitialized variable warning - * 629522 : [rtpjpegpay] add support for Define Restart Interval (DRI) - * 629839 : [qtdemux] Update xmp tags parsing - * 629896 : Error compiling raw1394 (without iec61883) - * 630088 : [flvdemux] jerky h.264 video playback - * 630205 : [icydemux] Forward tag events downstrem - * 630256 : rtph264-pay/depay: doesn't respect timestamps from incomming buffers - * 630317 : Getting pulsesink device names doesn't work like for alsasink - * 630378 : speexenc/speexdec crash with MSVC - * 630446 : rtpmanager: provide additional statistics - * 630447 : rtpsession: relax third-party collision detection - * 630449 : rtpbin: Unlock before adding pad in new_payload_found - * 630451 : rtpbin: Handle rysnc of iterator when looking for free pad name - * 630452 : rtpbin: Make cleaning up sources in rtp_session_on_timeout MT safe - * 630457 : rtpmanager: packet lost should not be a warning. - * 630458 : level: avoid division by zero on silence - * 630500 : [rtspsrc] does rtsp setup message always need " /UDP " string? - * 630888 : v4l2sink does not cope with v4l2loopback kernel module - * 631082 : rtpjitterbuffer: improve document reference - * 631303 : [goom] qos warnings if source is GstAudioSrc - * 631330 : [flvmux][PATCH] Resend updated onMetada tag when tags changes in streamable mode - * 631996 : [h264depay] regression: rtsp://stream.zoovision.com/KibaEp1n900.3gp - * 632548 : [rtspsrc] regression; fails to report duration - * 632553 : --disable-gconf still tries to install schemas - * 632682 : [matroskademux] Handle missing CodecPrivate for Vorbis/Theora - * 632945 : rtph264depay in access-unit=true mode does not aggregate the delta unit flag correctly - * 633205 : Fix for navigation events in videoflip - * 633212 : [goom] Return not-negotiated when bps is unknown - * 633970 : [icydemux] broken taglist handling - * 635532 : rtspsrc: unexpected eos when using authentication (regression) - * 635843 : [rtph264depay] segfault on empty payload - * 636179 : [deinterlace] Fields in wrong order - * 626463 : [matroskademux] " reading large block of size 14688496 not supported " - * 628894 : [matroskademux] sloppy reverse playback - * 633294 : deinterlace breaks some DVD menu scenarios + * 629418 : progressreport: add support for determining stream position from buffer timestamps instead of using queries + * 631516 : [navseek] Add support to change playback rate + * 632654 : [matroskamux] try to write timestamps in most of the outgoing buffers + * 632897 : flvmux does not set the correct nellymoser codec id + * 633280 : [icydemux][PATCH] icydemux: Send 'StreamUrl' metadata as GST_TAG_HOMEPAGE tag + * 634314 : pngdec hangs on faulty pngs + * 634391 : [v4l2src] add interlaced field to caps + * 634393 : v4l2src: Set top field first for interlaced captures + * 634910 : [rtph264pay] Implement bytestream scan mode + * 634928 : [qtdemux] report creation/modification time via metadata tag + * 635734 : jpegdec: infinite loop when playing back motion jpeg stream + * 636049 : ximagesrc: fix remote X and off by ones + * 636172 : imagefreeze: eos is not passed before a buffer arrives + * 636234 : [wavparse] dts 6ch played as stereo 16 bit pcm if DTS frame starts at non-zero offset + * 636621 : flvdemux: doesn't set the right sample rate for aac audio + * 636784 : [qtdemux] GST_QUERY_CONVERT implementation for qtdemux + * 637060 : matroskademux: errors out on 13MB blocks when streaming + * 637686 : [jpegenc] Improve sinkpad getcaps results + * 638019 : [matroskademux] some matroska files are not specifying DocType + * 638072 : build failure: rtpsource.c: error: 'have_rb' may be used uninitialized in this function + * 638535 : id3demux: multiple genres as per ID3v2.4 not supported correctly + * 638569 : cacasink crashes when given 15-bit video. + * 639240 : pulsesink: PLAYING- > PAUSED- > PLAYING transition causes dropout + * 639321 : deinterlace: field{1,3} scanline pointers seem to be off by one field line + * 639339 : v4l2: fails to build with older kernels due to missing V4L_FIELD_INTERLACED_{TB,BT} + * 639516 : muxers: fix setting src pad caps + * 639740 : [pulsesink] doesn't uncork in some cases during reverse playback + * 640028 : [qtdemux] crash on malformed mov stream + * 640063 : rtph264depay: leaks codec data buffer in byte-stream=false mode + * 640064 : rtspsrc memory leak + * 640080 : rtspsrc: fails to error out properly on network failure + * 623063 : [jpegdec] add " max-errors " property Download @@ -202,34 +181,38 @@ Applications Contributors to this release * Alessandro Decina - * American Dynamics * Andoni Morales Alastruey + * Andy Wingo * Arun Raghavan - * Bastien Nocera + * Benjamin Gaignard + * Benjamin Otte + * Christian Schaller * David Hoyt * David Schleef * Edward Hervey - * Havard Graff - * IOhannes m zmölnig + * Erich Schubert + * Guillaume Emont + * Iain Holmes * Jan Schmidt - * Jonathan Matthew + * Janne Grunau + * Johan Dahlin + * Kishore Arepalli + * Leif Johnson + * Marc-André Lureau * Mark Nauwelaerts - * Olivier Crête - * Pascal Buhler - * Pavel Kostyuchenko - * Philip Jägenstedt - * Philippe Normand - * René Stadler - * Robert Swain + * Paul Davis + * Rob Clark + * Ronald S. Bultje * Sebastian Dröge - * Sjoerd Simons * Stefan Kost + * Steve Baker + * Stéphane Loeuillet + * Tambet Ingo * Thiago Santos - * Thibault Saunier - * Thijs Vermeir + * Thomas Vander Stichele * Tim-Philipp Müller - * Trond Andersen - * Vladimir Eremeev + * Tom Janiszewski + * Tristan Matthews + * Vincent Penquerc'h * Wim Taymans - * Zaheer Abbas Merali   \ No newline at end of file diff --git a/common b/common index 011bcc8a0f..1de7f6ab2d 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 011bcc8a0fc7f798ee874a7ba899123fb2470e22 +Subproject commit 1de7f6ab2d4bc1af69f06079cf0f4e2cbbfdc178 diff --git a/configure.ac b/configure.ac index 5709092c55..d5e3afacc6 100644 --- a/configure.ac +++ b/configure.ac @@ -748,6 +748,14 @@ AG_GST_CHECK_FEATURE(HAL, [HAL libraries], halelements, [ AG_GST_PKG_CHECK_MODULES(HAL, [hal >= 0.5.6, dbus-1 >= 0.32]) ]) +dnl *** Jack *** +translit(dnm, m, l) AM_CONDITIONAL(USE_JACK, true) +AG_GST_CHECK_FEATURE(JACK, Jack, jack, [ + PKG_CHECK_MODULES(JACK, jack >= 0.99.10, HAVE_JACK="yes", HAVE_JACK="no") + AC_SUBST(JACK_CFLAGS) + AC_SUBST(JACK_LIBS) +]) + dnl *** jpeg *** dnl FIXME: we could use header checks here as well IMO translit(dnm, m, l) AM_CONDITIONAL(USE_JPEG, true) @@ -1020,6 +1028,7 @@ AM_CONDITIONAL(USE_GCONFTOOL, false) AM_CONDITIONAL(USE_GDK_PIXBUF, false) AM_CONDITIONAL(USE_GST_V4L2, false) AM_CONDITIONAL(USE_HAL, false) +AM_CONDITIONAL(USE_JACK, false) AM_CONDITIONAL(USE_JPEG, false) AM_CONDITIONAL(USE_LIBCACA, false) AM_CONDITIONAL(USE_LIBDV, false) @@ -1144,7 +1153,6 @@ gst/wavenc/Makefile gst/wavparse/Makefile gst/flx/Makefile gst/y4m/Makefile -ext/jpeg/Makefile ext/Makefile ext/aalib/Makefile ext/annodex/Makefile @@ -1155,6 +1163,8 @@ ext/flac/Makefile ext/gconf/Makefile ext/gdk_pixbuf/Makefile ext/hal/Makefile +ext/jack/Makefile +ext/jpeg/Makefile ext/libcaca/Makefile ext/libpng/Makefile ext/pulse/Makefile @@ -1180,6 +1190,7 @@ tests/check/Makefile tests/examples/Makefile tests/examples/audiofx/Makefile tests/examples/equalizer/Makefile +tests/examples/jack/Makefile tests/examples/level/Makefile tests/examples/pulse/Makefile tests/examples/rtp/Makefile @@ -1236,7 +1247,7 @@ sed \ -e 's/.* PLUGINDIR$/#ifdef _DEBUG\n# define PLUGINDIR PREFIX "\\\\debug\\\\lib\\\\gstreamer-0.11"\n#else\n# define PLUGINDIR PREFIX "\\\\lib\\\\gstreamer-0.11"\n#endif/' \ -e 's/.* USE_BINARY_REGISTRY$/#define USE_BINARY_REGISTRY/' \ -e 's/.* VERSION$/#define VERSION "'$VERSION'"/' \ - -e "s/.* DEFAULT_AUDIOSINK$/#define DEFAULT_AUDIOSINK \"directaudiosink\"/" \ + -e "s/.* DEFAULT_AUDIOSINK$/#define DEFAULT_AUDIOSINK \"directsoundsink\"/" \ -e "s/.* DEFAULT_AUDIOSRC$/#define DEFAULT_AUDIOSRC \"audiotestsrc\"/" \ -e "s/.* DEFAULT_VIDEOSRC$/#define DEFAULT_VIDEOSRC \"videotestsrc\"/" \ -e "s/.* DEFAULT_VISUALIZER$/#define DEFAULT_VISUALIZER \"goom\"/" \ diff --git a/docs/plugins/Makefile.am b/docs/plugins/Makefile.am index 6598f4db50..975e465ab9 100644 --- a/docs/plugins/Makefile.am +++ b/docs/plugins/Makefile.am @@ -94,6 +94,8 @@ EXTRA_HFILES = \ $(top_srcdir)/ext/gdk_pixbuf/gstgdkpixbufsink.h \ $(top_srcdir)/ext/hal/gsthalaudiosink.h \ $(top_srcdir)/ext/hal/gsthalaudiosrc.h \ + $(top_srcdir)/ext/jack/gstjackaudiosrc.h \ + $(top_srcdir)/ext/jack/gstjackaudiosink.h \ $(top_srcdir)/ext/jpeg/gstjpegdec.h \ $(top_srcdir)/ext/jpeg/gstjpegenc.h \ $(top_srcdir)/ext/jpeg/gstsmokedec.h \ diff --git a/docs/plugins/gst-plugins-good-plugins-docs.sgml b/docs/plugins/gst-plugins-good-plugins-docs.sgml index 1b7136b796..ade8dfde20 100644 --- a/docs/plugins/gst-plugins-good-plugins-docs.sgml +++ b/docs/plugins/gst-plugins-good-plugins-docs.sgml @@ -94,6 +94,8 @@ + + @@ -206,6 +208,7 @@ + diff --git a/docs/plugins/gst-plugins-good-plugins-sections.txt b/docs/plugins/gst-plugins-good-plugins-sections.txt index c643d47713..60d2c45a2d 100644 --- a/docs/plugins/gst-plugins-good-plugins-sections.txt +++ b/docs/plugins/gst-plugins-good-plugins-sections.txt @@ -1113,6 +1113,36 @@ GstInterleaveFunc gst_interleave_get_type +
+element-jackaudiosrc +jackaudiosrc +GstJackAudioSrc + +GstJackAudioSrcClass +GST_JACK_AUDIO_SRC +GST_JACK_AUDIO_SRC_CLASS +GST_JACK_AUDIO_SRC_GET_CLASS +GST_IS_JACK_AUDIO_SRC +GST_IS_JACK_AUDIO_SRC_CLASS +GST_TYPE_JACK_AUDIO_SRC +gst_jack_audio_src_get_type +
+ +
+element-jackaudiosink +jackaudiosink +GstJackAudioSink + +GstJackAudioSinkClass +GST_JACK_AUDIO_SINK +GST_JACK_AUDIO_SINK_CLASS +GST_JACK_AUDIO_SINK_GET_CLASS +GST_IS_JACK_AUDIO_SINK +GST_IS_JACK_AUDIO_SINK_CLASS +GST_TYPE_JACK_AUDIO_SINK +gst_jack_audio_sink_get_type +
+
element-jpegdec jpegdec diff --git a/docs/plugins/gst-plugins-good-plugins.args b/docs/plugins/gst-plugins-good-plugins.args index d0ac7fe073..3553b914d4 100644 --- a/docs/plugins/gst-plugins-good-plugins.args +++ b/docs/plugins/gst-plugins-good-plugins.args @@ -745,7 +745,7 @@ rw Buffer Mode Control the buffering algorithm in use. -Slave receiver to sender clock +Choose mode depending on stream live @@ -765,7 +765,7 @@ rw UDP Buffer Size Size of the kernel UDP receive buffer in bytes, 0=default. -0 +524288 @@ -1998,6 +1998,16 @@ "auto" + +GstProgressReport::do-query +gboolean + +rw +Use a query instead of buffer metadata to determine stream position +Use a query instead of buffer metadata to determine stream position. +TRUE + + GstNavSeek::seek-offset gdouble @@ -2518,6 +2528,16 @@ TRUE + +GstMultiUDPSink::buffer-size +gint +>= 0 +rw +Buffer Size +Size of the kernel send buffer in bytes, 0=default. +0 + + GstCmmlDec::wait-clip-end-time gboolean @@ -2688,6 +2708,16 @@ TRUE + +GstXImageSrc::remote +gboolean + +rw +Remote dispay +Whether the display is remote. +FALSE + + GstVideoBalance::brightness gdouble @@ -2768,6 +2798,16 @@ Faster, less accurate integer method + +GstJpegDec::max-errors +gint +>= G_MAXULONG +rw +Maximum Consecutive Decoding Errors +Error out after receiving N consecutive decoding errors (-1 = never fail, 0 = automatic, 1 = fail on first error). +0 + + GstRTPiLBCDepay::mode iLBCMode @@ -19674,7 +19714,7 @@ rw Byte Stream -Generate byte stream format of NALU. +Generate byte stream format of NALU (deprecated; use caps). TRUE @@ -19684,7 +19724,7 @@ rw Access Unit -Merge NALU into AU (picture). +Merge NALU into AU (picture) (deprecated; use caps). FALSE @@ -19838,6 +19878,16 @@ + +GstPulseSrc::client +gchar* + +rw +Client +The PulseAudio client_name_to_use. +"" + + GstPulseMixer::device gchar* @@ -20315,7 +20365,7 @@ rw Method Deinterlace Method. -Motion Adaptive: Advanced Detection +Television: Full resolution @@ -20805,7 +20855,7 @@ rw Queue size Number of buffers to be enqueud in the driver in streaming mode. -8 +12 @@ -20848,6 +20898,56 @@ 0 + +GstV4l2Sink::crop-height +guint + +rw +Crop height +The height of the video crop; default is equal to negotiated image height. +0 + + + +GstV4l2Sink::crop-left +gint + +rw +Crop left +The leftmost (x) coordinate of the video crop; top left corner of image is 0,0. +0 + + + +GstV4l2Sink::crop-top +gint + +rw +Crop top +The topmost (y) coordinate of the video crop; top left corner of image is 0,0. +0 + + + +GstV4l2Sink::crop-width +guint + +rw +Crop width +The width of the video crop; default is equal to negotiated image width. +0 + + + +GstV4l2Sink::min-queued-bufs +guint +<= 16 +rw +Minimum queued bufs +Minimum number of queued bufs; v4l2sink won't dqbuf if the driver doesn't have more than this number (which normally you shouldn't change). +1 + + GstShapeWipe::border gfloat @@ -21018,3 +21118,83 @@ Checker pattern + +GstRtpJ2KPay::buffer-list +gboolean + +rw +Buffer List +Use Buffer Lists. +TRUE + + + +GstRtpJ2KDepay::buffer-list +gboolean + +rw +Buffer List +Use Buffer Lists. +TRUE + + + +GstJackAudioSrc::client +JackClient* + +rw +JackClient +Handle for jack client. + + + + +GstJackAudioSrc::connect +GstJackConnect + +rw +Connect +Specify how the input ports will be connected. +Automatically connect ports to physical ports + + + +GstJackAudioSrc::server +gchar* + +rw +Server +The Jack server to connect to (NULL = default). +NULL + + + +GstJackAudioSink::client +JackClient* + +rw +JackClient +Handle for jack client. + + + + +GstJackAudioSink::connect +GstJackConnect + +rw +Connect +Specify how the output ports will be connected. +Automatically connect ports to physical ports + + + +GstJackAudioSink::server +gchar* + +rw +Server +The Jack server to connect to (NULL = default). +NULL + + diff --git a/docs/plugins/gst-plugins-good-plugins.hierarchy b/docs/plugins/gst-plugins-good-plugins.hierarchy index 334128a2bc..5585c4a857 100644 --- a/docs/plugins/gst-plugins-good-plugins.hierarchy +++ b/docs/plugins/gst-plugins-good-plugins.hierarchy @@ -30,6 +30,7 @@ GObject GstRtpG723Depay GstRtpG726Depay GstRtpG729Depay + GstRtpGSTDepay GstRtpH263Depay GstRtpH263PDepay GstRtpH264Depay @@ -70,8 +71,10 @@ GObject GstRTPGSMPay GstRTPMP2TPay GstRTPMPVPay + GstRtpAC3Pay GstRtpAMRPay GstRtpCELTPay + GstRtpGSTPay GstRtpH263PPay GstRtpH263Pay GstRtpH264Pay @@ -92,6 +95,7 @@ GObject GstEsdSink GstOss4Sink GstOssSink + GstJackAudioSink GstPulseSink GstCACASink GstDynUDPSink @@ -110,6 +114,7 @@ GObject GstOss4Source GstOssSrc GstPulseSrc + GstJackAudioSrc GstDV1394Src GstHDV1394Src GstMultiFileSrc @@ -218,6 +223,7 @@ GObject GstMatroskaDemux GstMatroskaMux GstWebMMux + GstMonoscope GstMuLawDec GstMuLawEnc GstMultipartDemux @@ -269,6 +275,8 @@ GObject GstRingBuffer GstAudioSinkRingBuffer GstAudioSrcRingBuffer + GstJackAudioSinkRingBuffer + GstJackAudioSrcRingBuffer GstTask GstTaskPool GstSignalObject @@ -282,6 +290,7 @@ GInterface GstColorBalance GstImplementsInterface GstMixer + GstNavigation GstPreset GstPropertyProbe GstStreamVolume @@ -289,3 +298,4 @@ GInterface GstTuner GstURIHandler GstVideoOrientation + GstXOverlay diff --git a/docs/plugins/gst-plugins-good-plugins.interfaces b/docs/plugins/gst-plugins-good-plugins.interfaces index a7edf25999..7d98456987 100644 --- a/docs/plugins/gst-plugins-good-plugins.interfaces +++ b/docs/plugins/gst-plugins-good-plugins.interfaces @@ -19,7 +19,7 @@ GstRgVolume GstChildProxy GstAspectRatioCrop GstChildProxy GstPulseSink GstStreamVolume GstImplementsInterface GstPropertyProbe GstOss4Sink GstStreamVolume GstPropertyProbe -GstV4l2Sink GstImplementsInterface GstColorBalance GstVideoOrientation GstPropertyProbe +GstV4l2Sink GstImplementsInterface GstXOverlay GstNavigation GstColorBalance GstVideoOrientation GstPropertyProbe GstShout2send GstTagSetter GstUDPSink GstURIHandler GstDV1394Src GstURIHandler GstPropertyProbe diff --git a/docs/plugins/gst-plugins-good-plugins.prerequisites b/docs/plugins/gst-plugins-good-plugins.prerequisites index fa0a1d51c6..3f9042f552 100644 --- a/docs/plugins/gst-plugins-good-plugins.prerequisites +++ b/docs/plugins/gst-plugins-good-plugins.prerequisites @@ -6,4 +6,5 @@ GstMixer GstImplementsInterface GstElement GstTuner GstImplementsInterface GstElement GstColorBalance GstImplementsInterface GstElement GstVideoOrientation GstImplementsInterface GstElement +GstXOverlay GstImplementsInterface GstElement GIcon GObject diff --git a/docs/plugins/inspect/plugin-1394.xml b/docs/plugins/inspect/plugin-1394.xml index 971756f303..7cef67174a 100644 --- a/docs/plugins/inspect/plugin-1394.xml +++ b/docs/plugins/inspect/plugin-1394.xml @@ -3,7 +3,7 @@ Source for video data via IEEE1394 interface ../../ext/raw1394/.libs/libgst1394.so libgst1394.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-aasink.xml b/docs/plugins/inspect/plugin-aasink.xml index bb4c9f8562..1ed508bf95 100644 --- a/docs/plugins/inspect/plugin-aasink.xml +++ b/docs/plugins/inspect/plugin-aasink.xml @@ -3,7 +3,7 @@ ASCII Art video sink ../../ext/aalib/.libs/libgstaasink.so libgstaasink.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-alaw.xml b/docs/plugins/inspect/plugin-alaw.xml index c1fd5fbb52..8e0ace5bec 100644 --- a/docs/plugins/inspect/plugin-alaw.xml +++ b/docs/plugins/inspect/plugin-alaw.xml @@ -3,7 +3,7 @@ ALaw audio conversion routines ../../gst/law/.libs/libgstalaw.so libgstalaw.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-alpha.xml b/docs/plugins/inspect/plugin-alpha.xml index 57fbba62e2..5e72d1580b 100644 --- a/docs/plugins/inspect/plugin-alpha.xml +++ b/docs/plugins/inspect/plugin-alpha.xml @@ -3,7 +3,7 @@ adds an alpha channel to video - constant or via chroma-keying ../../gst/alpha/.libs/libgstalpha.so libgstalpha.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-alphacolor.xml b/docs/plugins/inspect/plugin-alphacolor.xml index 73c2daf9d8..5145144005 100644 --- a/docs/plugins/inspect/plugin-alphacolor.xml +++ b/docs/plugins/inspect/plugin-alphacolor.xml @@ -3,7 +3,7 @@ RGBA from/to AYUV colorspace conversion preserving the alpha channel ../../gst/alpha/.libs/libgstalphacolor.so libgstalphacolor.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-annodex.xml b/docs/plugins/inspect/plugin-annodex.xml index 4b6a03d197..757c17b3a3 100644 --- a/docs/plugins/inspect/plugin-annodex.xml +++ b/docs/plugins/inspect/plugin-annodex.xml @@ -3,7 +3,7 @@ annodex stream manipulation (info about annodex: http://www.annodex.net) ../../ext/annodex/.libs/libgstannodex.so libgstannodex.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-apetag.xml b/docs/plugins/inspect/plugin-apetag.xml index c6a1042120..1dc3d8e1b5 100644 --- a/docs/plugins/inspect/plugin-apetag.xml +++ b/docs/plugins/inspect/plugin-apetag.xml @@ -3,7 +3,7 @@ APEv1/2 tag reader ../../gst/apetag/.libs/libgstapetag.so libgstapetag.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-audiofx.xml b/docs/plugins/inspect/plugin-audiofx.xml index b7e9c553d5..2441f9387f 100644 --- a/docs/plugins/inspect/plugin-audiofx.xml +++ b/docs/plugins/inspect/plugin-audiofx.xml @@ -3,7 +3,7 @@ Audio effects plugin ../../gst/audiofx/.libs/libgstaudiofx.so libgstaudiofx.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-auparse.xml b/docs/plugins/inspect/plugin-auparse.xml index 53141b1b76..5981acefb5 100644 --- a/docs/plugins/inspect/plugin-auparse.xml +++ b/docs/plugins/inspect/plugin-auparse.xml @@ -3,7 +3,7 @@ parses au streams ../../gst/auparse/.libs/libgstauparse.so libgstauparse.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-autodetect.xml b/docs/plugins/inspect/plugin-autodetect.xml index fb0d15f61f..338849f3cb 100644 --- a/docs/plugins/inspect/plugin-autodetect.xml +++ b/docs/plugins/inspect/plugin-autodetect.xml @@ -3,7 +3,7 @@ Plugin contains auto-detection plugins for video/audio in- and outputs ../../gst/autodetect/.libs/libgstautodetect.so libgstautodetect.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-avi.xml b/docs/plugins/inspect/plugin-avi.xml index 3baa80cf69..8407f16c7d 100644 --- a/docs/plugins/inspect/plugin-avi.xml +++ b/docs/plugins/inspect/plugin-avi.xml @@ -3,7 +3,7 @@ AVI stream handling ../../gst/avi/.libs/libgstavi.so libgstavi.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-cacasink.xml b/docs/plugins/inspect/plugin-cacasink.xml index 7efbee0825..7fd0297df0 100644 --- a/docs/plugins/inspect/plugin-cacasink.xml +++ b/docs/plugins/inspect/plugin-cacasink.xml @@ -3,7 +3,7 @@ Colored ASCII Art video sink ../../ext/libcaca/.libs/libgstcacasink.so libgstcacasink.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-cairo.xml b/docs/plugins/inspect/plugin-cairo.xml index 9e4291190c..e240b369b2 100644 --- a/docs/plugins/inspect/plugin-cairo.xml +++ b/docs/plugins/inspect/plugin-cairo.xml @@ -3,7 +3,7 @@ Cairo-based elements ../../ext/cairo/.libs/libgstcairo.so libgstcairo.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-cutter.xml b/docs/plugins/inspect/plugin-cutter.xml index defe275701..cf7d7202cd 100644 --- a/docs/plugins/inspect/plugin-cutter.xml +++ b/docs/plugins/inspect/plugin-cutter.xml @@ -3,7 +3,7 @@ Audio Cutter to split audio into non-silent bits ../../gst/cutter/.libs/libgstcutter.so libgstcutter.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-debug.xml b/docs/plugins/inspect/plugin-debug.xml index f1f4127b33..a3bac01696 100644 --- a/docs/plugins/inspect/plugin-debug.xml +++ b/docs/plugins/inspect/plugin-debug.xml @@ -3,7 +3,7 @@ elements for testing and debugging ../../gst/debugutils/.libs/libgstdebug.so libgstdebug.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-deinterlace.xml b/docs/plugins/inspect/plugin-deinterlace.xml index 8c375e2405..35a5156783 100644 --- a/docs/plugins/inspect/plugin-deinterlace.xml +++ b/docs/plugins/inspect/plugin-deinterlace.xml @@ -3,7 +3,7 @@ Deinterlacer ../../gst/deinterlace/.libs/libgstdeinterlace.so libgstdeinterlace.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git @@ -12,7 +12,7 @@ deinterlace Deinterlacer - Filter/Video + Filter/Effect/Video/Deinterlace Deinterlace Methods ported from DScaler/TvTime Martin Eikermann <meiker@upb.de>, Sebastian Dröge <sebastian.droege@collabora.co.uk> diff --git a/docs/plugins/inspect/plugin-dv.xml b/docs/plugins/inspect/plugin-dv.xml index 0cee0f907c..772493b8b3 100644 --- a/docs/plugins/inspect/plugin-dv.xml +++ b/docs/plugins/inspect/plugin-dv.xml @@ -3,7 +3,7 @@ DV demuxer and decoder based on libdv (libdv.sf.net) ../../ext/dv/.libs/libgstdv.so libgstdv.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-efence.xml b/docs/plugins/inspect/plugin-efence.xml index 64d81053cd..a0d91632d7 100644 --- a/docs/plugins/inspect/plugin-efence.xml +++ b/docs/plugins/inspect/plugin-efence.xml @@ -3,7 +3,7 @@ This element converts a stream of normal GStreamer buffers into a stream of buffers that are allocated in such a way that out-of-bounds access to data in the buffer is more likely to cause segmentation faults. This allocation method is very similar to the debugging tool "Electric Fence". ../../gst/debugutils/.libs/libgstefence.so libgstefence.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-effectv.xml b/docs/plugins/inspect/plugin-effectv.xml index bcc608be43..5011b2ef06 100644 --- a/docs/plugins/inspect/plugin-effectv.xml +++ b/docs/plugins/inspect/plugin-effectv.xml @@ -3,7 +3,7 @@ effect plugins from the effectv project ../../gst/effectv/.libs/libgsteffectv.so libgsteffectv.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-equalizer.xml b/docs/plugins/inspect/plugin-equalizer.xml index 5cda20e0f3..17ff647440 100644 --- a/docs/plugins/inspect/plugin-equalizer.xml +++ b/docs/plugins/inspect/plugin-equalizer.xml @@ -3,7 +3,7 @@ GStreamer audio equalizers ../../gst/equalizer/.libs/libgstequalizer.so libgstequalizer.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-esdsink.xml b/docs/plugins/inspect/plugin-esdsink.xml index 2c089284ad..b1ad8174f5 100644 --- a/docs/plugins/inspect/plugin-esdsink.xml +++ b/docs/plugins/inspect/plugin-esdsink.xml @@ -3,7 +3,7 @@ ESD Element Plugins ../../ext/esd/.libs/libgstesd.so libgstesd.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-flac.xml b/docs/plugins/inspect/plugin-flac.xml index d09a9974b3..e7c7860a38 100644 --- a/docs/plugins/inspect/plugin-flac.xml +++ b/docs/plugins/inspect/plugin-flac.xml @@ -3,7 +3,7 @@ The FLAC Lossless compressor Codec ../../ext/flac/.libs/libgstflac.so libgstflac.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-flv.xml b/docs/plugins/inspect/plugin-flv.xml index 0d5547fe9b..a251587ae9 100644 --- a/docs/plugins/inspect/plugin-flv.xml +++ b/docs/plugins/inspect/plugin-flv.xml @@ -3,7 +3,7 @@ FLV muxing and demuxing plugin ../../gst/flv/.libs/libgstflv.so libgstflv.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-flxdec.xml b/docs/plugins/inspect/plugin-flxdec.xml index ec51525cfb..d2402ed306 100644 --- a/docs/plugins/inspect/plugin-flxdec.xml +++ b/docs/plugins/inspect/plugin-flxdec.xml @@ -3,7 +3,7 @@ FLC/FLI/FLX video decoder ../../gst/flx/.libs/libgstflxdec.so libgstflxdec.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-gconfelements.xml b/docs/plugins/inspect/plugin-gconfelements.xml index e90f8e1b7b..49d897b667 100644 --- a/docs/plugins/inspect/plugin-gconfelements.xml +++ b/docs/plugins/inspect/plugin-gconfelements.xml @@ -3,7 +3,7 @@ elements wrapping the GStreamer/GConf audio/video output settings ../../ext/gconf/.libs/libgstgconfelements.so libgstgconfelements.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-gdkpixbuf.xml b/docs/plugins/inspect/plugin-gdkpixbuf.xml index cfe5c2b643..75e6154910 100644 --- a/docs/plugins/inspect/plugin-gdkpixbuf.xml +++ b/docs/plugins/inspect/plugin-gdkpixbuf.xml @@ -3,7 +3,7 @@ GdkPixbuf-based image decoder, scaler and sink ../../ext/gdk_pixbuf/.libs/libgstgdkpixbuf.so libgstgdkpixbuf.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-goom.xml b/docs/plugins/inspect/plugin-goom.xml index 9c1a9e5097..a45d8552ff 100644 --- a/docs/plugins/inspect/plugin-goom.xml +++ b/docs/plugins/inspect/plugin-goom.xml @@ -3,7 +3,7 @@ GOOM visualization filter ../../gst/goom/.libs/libgstgoom.so libgstgoom.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-goom2k1.xml b/docs/plugins/inspect/plugin-goom2k1.xml index d4b090ce62..f443b7bb8f 100644 --- a/docs/plugins/inspect/plugin-goom2k1.xml +++ b/docs/plugins/inspect/plugin-goom2k1.xml @@ -3,7 +3,7 @@ GOOM 2k1 visualization filter ../../gst/goom2k1/.libs/libgstgoom2k1.so libgstgoom2k1.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-gstrtpmanager.xml b/docs/plugins/inspect/plugin-gstrtpmanager.xml index 90932beeac..bc8012f16c 100644 --- a/docs/plugins/inspect/plugin-gstrtpmanager.xml +++ b/docs/plugins/inspect/plugin-gstrtpmanager.xml @@ -3,7 +3,7 @@ RTP session management plugin library ../../gst/rtpmanager/.libs/libgstrtpmanager.so libgstrtpmanager.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-halelements.xml b/docs/plugins/inspect/plugin-halelements.xml index 0a44fc15f4..68d00d5749 100644 --- a/docs/plugins/inspect/plugin-halelements.xml +++ b/docs/plugins/inspect/plugin-halelements.xml @@ -3,7 +3,7 @@ elements wrapping the GStreamer/HAL audio input/output devices ../../ext/hal/.libs/libgsthalelements.so libgsthalelements.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-icydemux.xml b/docs/plugins/inspect/plugin-icydemux.xml index 2c14795f34..5189eff17d 100644 --- a/docs/plugins/inspect/plugin-icydemux.xml +++ b/docs/plugins/inspect/plugin-icydemux.xml @@ -3,7 +3,7 @@ Demux ICY tags from a stream ../../gst/icydemux/.libs/libgsticydemux.so libgsticydemux.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-id3demux.xml b/docs/plugins/inspect/plugin-id3demux.xml index 6abe75fcf4..c3a3c79d18 100644 --- a/docs/plugins/inspect/plugin-id3demux.xml +++ b/docs/plugins/inspect/plugin-id3demux.xml @@ -3,7 +3,7 @@ Demux ID3v1 and ID3v2 tags from a file ../../gst/id3demux/.libs/libgstid3demux.so libgstid3demux.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-imagefreeze.xml b/docs/plugins/inspect/plugin-imagefreeze.xml index 26d92271af..b2ba988d37 100644 --- a/docs/plugins/inspect/plugin-imagefreeze.xml +++ b/docs/plugins/inspect/plugin-imagefreeze.xml @@ -3,7 +3,7 @@ Still frame stream generator ../../gst/imagefreeze/.libs/libgstimagefreeze.so libgstimagefreeze.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-interleave.xml b/docs/plugins/inspect/plugin-interleave.xml index 7c99bd0dc1..9d9f136852 100644 --- a/docs/plugins/inspect/plugin-interleave.xml +++ b/docs/plugins/inspect/plugin-interleave.xml @@ -3,7 +3,7 @@ Audio interleaver/deinterleaver ../../gst/interleave/.libs/libgstinterleave.so libgstinterleave.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-jack.xml b/docs/plugins/inspect/plugin-jack.xml new file mode 100644 index 0000000000..4d63a04c15 --- /dev/null +++ b/docs/plugins/inspect/plugin-jack.xml @@ -0,0 +1,43 @@ + + jack + JACK audio elements + ../../ext/jack/.libs/libgstjack.so + libgstjack.so + 0.10.27.1 + LGPL + gst-plugins-good + GStreamer Good Plug-ins git + Unknown package origin + + + jackaudiosink + Audio Sink (Jack) + Sink/Audio + Output audio to a JACK server + Wim Taymans <wim.taymans@gmail.com> + + + sink + sink + always +
audio/x-raw-float, endianness=(int)1234, width=(int)32, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 2147483647 ]
+
+
+
+ + jackaudiosrc + Audio Source (Jack) + Source/Audio + Captures audio from a JACK server + Tristan Matthews <tristan@sat.qc.ca> + + + src + source + always +
audio/x-raw-float, endianness=(int)1234, width=(int)32, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 2147483647 ]
+
+
+
+
+
\ No newline at end of file diff --git a/docs/plugins/inspect/plugin-jpeg.xml b/docs/plugins/inspect/plugin-jpeg.xml index 88ac09b67b..4f897e60d1 100644 --- a/docs/plugins/inspect/plugin-jpeg.xml +++ b/docs/plugins/inspect/plugin-jpeg.xml @@ -3,7 +3,7 @@ JPeg plugin library ../../ext/jpeg/.libs/libgstjpeg.so libgstjpeg.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-level.xml b/docs/plugins/inspect/plugin-level.xml index a45421ebcc..b7e9bb87bf 100644 --- a/docs/plugins/inspect/plugin-level.xml +++ b/docs/plugins/inspect/plugin-level.xml @@ -3,7 +3,7 @@ Audio level plugin ../../gst/level/.libs/libgstlevel.so libgstlevel.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-matroska.xml b/docs/plugins/inspect/plugin-matroska.xml index 7e09a94a9d..ac63701a3f 100644 --- a/docs/plugins/inspect/plugin-matroska.xml +++ b/docs/plugins/inspect/plugin-matroska.xml @@ -3,7 +3,7 @@ Matroska and WebM stream handling ../../gst/matroska/.libs/libgstmatroska.so libgstmatroska.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git @@ -53,13 +53,13 @@ audio_%d sink request -
audio/mpeg, mpegversion=(int)1, layer=(int)[ 1, 3 ], stream-format=(string){ raw }, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/mpeg, mpegversion=(int){ 2, 4 }, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-ac3, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-vorbis, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-flac, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-speex, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-raw-int, width=(int)8, depth=(int)8, signed=(boolean)false, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int){ 4321, 1234 }, signed=(boolean)true, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-raw-int, width=(int)24, depth=(int)24, endianness=(int){ 4321, 1234 }, signed=(boolean)true, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-raw-int, width=(int)32, depth=(int)32, endianness=(int){ 4321, 1234 }, signed=(boolean)true, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-raw-float, width=(int)[ 32, 64 ], endianness=(int)1234, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-tta, width=(int){ 8, 16, 24 }, channels=(int){ 1, 2 }, rate=(int)[ 8000, 96000 ]; audio/x-pn-realaudio, raversion=(int){ 1, 2, 8 }, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-wma, wmaversion=(int)[ 1, 3 ], block_align=(int)[ 0, 65535 ], bitrate=(int)[ 0, 524288 ], channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]
+
audio/mpeg, mpegversion=(int)1, layer=(int)[ 1, 3 ], stream-format=(string){ raw }, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/mpeg, mpegversion=(int){ 2, 4 }, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-ac3, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-eac3, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-dts, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-vorbis, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-flac, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-speex, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-raw-int, width=(int)8, depth=(int)8, signed=(boolean)false, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int){ 4321, 1234 }, signed=(boolean)true, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-raw-int, width=(int)24, depth=(int)24, endianness=(int){ 4321, 1234 }, signed=(boolean)true, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-raw-int, width=(int)32, depth=(int)32, endianness=(int){ 4321, 1234 }, signed=(boolean)true, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-raw-float, width=(int)[ 32, 64 ], endianness=(int)1234, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-tta, width=(int){ 8, 16, 24 }, channels=(int){ 1, 2 }, rate=(int)[ 8000, 96000 ]; audio/x-pn-realaudio, raversion=(int){ 1, 2, 8 }, channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]; audio/x-wma, wmaversion=(int)[ 1, 3 ], block_align=(int)[ 0, 65535 ], bitrate=(int)[ 0, 524288 ], channels=(int)[ 1, 2147483647 ], rate=(int)[ 1, 2147483647 ]
subtitle_%d sink request -
ANY
+
subtitle/x-kate
video_%d diff --git a/docs/plugins/inspect/plugin-monoscope.xml b/docs/plugins/inspect/plugin-monoscope.xml index 8d6951fdd4..0771febb2b 100644 --- a/docs/plugins/inspect/plugin-monoscope.xml +++ b/docs/plugins/inspect/plugin-monoscope.xml @@ -3,10 +3,10 @@ Monoscope visualization ../../gst/monoscope/.libs/libgstmonoscope.so libgstmonoscope.so - 0.10.24.5 + 0.10.27.1 LGPL gst-plugins-good - GStreamer Good Plug-ins prerelease + GStreamer Good Plug-ins git Unknown package origin diff --git a/docs/plugins/inspect/plugin-mulaw.xml b/docs/plugins/inspect/plugin-mulaw.xml index e7b2ccb0b5..2b9320a635 100644 --- a/docs/plugins/inspect/plugin-mulaw.xml +++ b/docs/plugins/inspect/plugin-mulaw.xml @@ -3,7 +3,7 @@ MuLaw audio conversion routines ../../gst/law/.libs/libgstmulaw.so libgstmulaw.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-multifile.xml b/docs/plugins/inspect/plugin-multifile.xml index dc9da5e01d..434b1c0944 100644 --- a/docs/plugins/inspect/plugin-multifile.xml +++ b/docs/plugins/inspect/plugin-multifile.xml @@ -3,7 +3,7 @@ Reads/Writes buffers from/to sequentially named files ../../gst/multifile/.libs/libgstmultifile.so libgstmultifile.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-multipart.xml b/docs/plugins/inspect/plugin-multipart.xml index d3dcea5bed..7ee0e82268 100644 --- a/docs/plugins/inspect/plugin-multipart.xml +++ b/docs/plugins/inspect/plugin-multipart.xml @@ -3,7 +3,7 @@ multipart stream manipulation ../../gst/multipart/.libs/libgstmultipart.so libgstmultipart.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-navigationtest.xml b/docs/plugins/inspect/plugin-navigationtest.xml index 2c40feca8a..3ab99095db 100644 --- a/docs/plugins/inspect/plugin-navigationtest.xml +++ b/docs/plugins/inspect/plugin-navigationtest.xml @@ -3,7 +3,7 @@ Template for a video filter ../../gst/debugutils/.libs/libgstnavigationtest.so libgstnavigationtest.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-oss4.xml b/docs/plugins/inspect/plugin-oss4.xml index ed11ad234c..1a53012ea2 100644 --- a/docs/plugins/inspect/plugin-oss4.xml +++ b/docs/plugins/inspect/plugin-oss4.xml @@ -3,7 +3,7 @@ Open Sound System (OSS) version 4 support for GStreamer ../../sys/oss4/.libs/libgstoss4audio.so libgstoss4audio.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-ossaudio.xml b/docs/plugins/inspect/plugin-ossaudio.xml index 5ce429813c..37ba51bad6 100644 --- a/docs/plugins/inspect/plugin-ossaudio.xml +++ b/docs/plugins/inspect/plugin-ossaudio.xml @@ -3,7 +3,7 @@ OSS (Open Sound System) support for GStreamer ../../sys/oss/.libs/libgstossaudio.so libgstossaudio.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-png.xml b/docs/plugins/inspect/plugin-png.xml index 1e98203d73..698e4ba215 100644 --- a/docs/plugins/inspect/plugin-png.xml +++ b/docs/plugins/inspect/plugin-png.xml @@ -3,7 +3,7 @@ PNG plugin library ../../ext/libpng/.libs/libgstpng.so libgstpng.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-pulseaudio.xml b/docs/plugins/inspect/plugin-pulseaudio.xml index a453c9dc22..d907f1068e 100644 --- a/docs/plugins/inspect/plugin-pulseaudio.xml +++ b/docs/plugins/inspect/plugin-pulseaudio.xml @@ -3,7 +3,7 @@ PulseAudio plugin library ../../ext/pulse/.libs/libgstpulse.so libgstpulse.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-quicktime.xml b/docs/plugins/inspect/plugin-quicktime.xml index 9f7889f850..d71f20e15f 100644 --- a/docs/plugins/inspect/plugin-quicktime.xml +++ b/docs/plugins/inspect/plugin-quicktime.xml @@ -3,7 +3,7 @@ Quicktime support ../../gst/qtdemux/.libs/libgstqtdemux.so libgstqtdemux.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-replaygain.xml b/docs/plugins/inspect/plugin-replaygain.xml index 6749df4393..5cf289d496 100644 --- a/docs/plugins/inspect/plugin-replaygain.xml +++ b/docs/plugins/inspect/plugin-replaygain.xml @@ -3,7 +3,7 @@ ReplayGain volume normalization ../../gst/replaygain/.libs/libgstreplaygain.so libgstreplaygain.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-rtp.xml b/docs/plugins/inspect/plugin-rtp.xml index d3c27203f2..dd4c0051bf 100644 --- a/docs/plugins/inspect/plugin-rtp.xml +++ b/docs/plugins/inspect/plugin-rtp.xml @@ -3,7 +3,7 @@ Real-time protocol plugins ../../gst/rtp/.libs/libgstrtp.so libgstrtp.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git @@ -12,7 +12,7 @@ asteriskh263 RTP Asterisk H263 depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts H263 video from RTP and encodes in Asterisk H263 format Neil Stratford <neils@vipadia.com> @@ -33,7 +33,7 @@ rtpL16depay RTP audio depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts raw audio from RTP packets Zeeshan Ali <zak147@yahoo.com>,Wim Taymans <wim.taymans@gmail.com> @@ -54,7 +54,7 @@ rtpL16pay RTP audio payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encode Raw audio into RTP packets (RFC 3551) Wim Taymans <wim.taymans@gmail.com> @@ -75,7 +75,7 @@ rtpac3depay RTP AC3 depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts AC3 audio from RTP packets (RFC 4184) Wim Taymans <wim.taymans@gmail.com> @@ -93,10 +93,31 @@
+ + rtpac3pay + RTP AC3 audio payloader + Codec/Payloader/Network/RTP + Payload AC3 audio as RTP packets (RFC 4184) + Wim Taymans <wim.taymans@gmail.com> + + + sink + sink + always +
audio/ac3; audio/x-ac3
+
+ + src + source + always +
application/x-rtp, media=(string)audio, payload=(int)[ 96, 127 ], clock-rate=(int){ 32000, 44100, 48000 }, encoding-name=(string)AC3
+
+
+
rtpamrdepay RTP AMR depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts AMR or AMR-WB audio from RTP packets (RFC 3267) Wim Taymans <wim.taymans@gmail.com> @@ -117,7 +138,7 @@ rtpamrpay RTP AMR payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encode AMR or AMR-WB audio into RTP packets (RFC 3267) Wim Taymans <wim.taymans@gmail.com> @@ -138,7 +159,7 @@ rtpbvdepay RTP BroadcomVoice depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts BroadcomVoice audio from RTP packets (RFC 4298) Wim Taymans <wim.taymans@collabora.co.uk> @@ -159,7 +180,7 @@ rtpbvpay RTP BV Payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Packetize BroadcomVoice audio streams into RTP packets (RFC 4298) Wim Taymans <wim.taymans@collabora.co.uk> @@ -180,7 +201,7 @@ rtpceltdepay RTP CELT depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts CELT audio from RTP packets Wim Taymans <wim.taymans@gmail.com> @@ -201,7 +222,7 @@ rtpceltpay RTP CELT payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encodes CELT audio into a RTP packet Wim Taymans <wim.taymans@gmail.com> @@ -222,7 +243,7 @@ rtpdepay Dummy RTP session manager - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Accepts raw RTP and RTCP packets and sends them forward Wim Taymans <wim.taymans@gmail.com> @@ -255,7 +276,7 @@ rtpdvdepay RTP DV Depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Depayloads DV from RTP packets (RFC 3189) Marcel Moreaux <marcelm@spacelabs.nl>, Wim Taymans <wim.taymans@gmail.com> @@ -276,7 +297,7 @@ rtpdvpay RTP DV Payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payloads DV into RTP packets (RFC 3189) Marcel Moreaux <marcelm@spacelabs.nl>, Wim Taymans <wim.taymans@gmail.com> @@ -297,7 +318,7 @@ rtpg722depay RTP audio depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts G722 audio from RTP packets Wim Taymans <wim.taymans@gmail.com> @@ -318,7 +339,7 @@ rtpg722pay RTP audio payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encode Raw audio into RTP packets (RFC 3551) Wim Taymans <wim.taymans@gmail.com> @@ -339,7 +360,7 @@ rtpg723depay RTP G.723 depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts G.723 audio from RTP packets (RFC 3551) Wim Taymans <wim.taymans@gmail.com> @@ -360,7 +381,7 @@ rtpg723pay RTP G.723 payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Packetize G.723 audio into RTP packets Wim Taymans <wim.taymans@gmail.com> @@ -381,7 +402,7 @@ rtpg726depay RTP G.726 depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts G.726 audio from RTP packets Axis Communications <dev-gstreamer@axis.com> @@ -402,7 +423,7 @@ rtpg726pay RTP G.726 payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encodes G.726 audio into a RTP packet Axis Communications <dev-gstreamer@axis.com> @@ -423,7 +444,7 @@ rtpg729depay RTP G.729 depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts G.729 audio from RTP packets (RFC 3551) Laurent Glayal <spglegle@yahoo.fr> @@ -444,7 +465,7 @@ rtpg729pay RTP G.729 payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Packetize G.729 audio into RTP packets Olivier Crete <olivier.crete@collabora.co.uk> @@ -465,7 +486,7 @@ rtpgsmdepay RTP GSM depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts GSM audio from RTP packets Zeeshan Ali <zeenix@gmail.com> @@ -486,7 +507,7 @@ rtpgsmpay RTP GSM payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encodes GSM audio into a RTP packet Zeeshan Ali <zeenix@gmail.com> @@ -504,10 +525,52 @@ + + rtpgstdepay + GStreamer depayloader + Codec/Depayloader/Network + Extracts GStreamer buffers from RTP packets + Wim Taymans <wim.taymans@gmail.com> + + + sink + sink + always +
application/x-rtp, media=(string)application, payload=(int)[ 96, 127 ], clock-rate=(int)90000, encoding-name=(string)X-GST
+
+ + src + source + always +
ANY
+
+
+
+ + rtpgstpay + RTP GStreamer payloader + Codec/Payloader/Network/RTP + Payload GStreamer buffers as RTP packets + Wim Taymans <wim.taymans@gmail.com> + + + sink + sink + always +
ANY
+
+ + src + source + always +
application/x-rtp, media=(string)application, payload=(int)[ 96, 127 ], clock-rate=(int)90000, encoding-name=(string)X-GST
+
+
+
rtph263depay RTP H263 depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts H263 video from RTP packets (RFC 2190) Philippe Kalaf <philippe.kalaf@collabora.co.uk>, Edward Hervey <bilboed@bilboed.com> @@ -528,7 +591,7 @@ rtph263pay RTP H263 packet payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encodes H263 video in RTP packets (RFC 2190) Neil Stratford <neils@vipadia.com>Dejan Sakelsak <dejan.sakelsak@marand.si> @@ -549,7 +612,7 @@ rtph263pdepay RTP H263 depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts H263/+/++ video from RTP packets (RFC 4629) Wim Taymans <wim.taymans@gmail.com> @@ -570,7 +633,7 @@ rtph263ppay RTP H263 payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encodes H263/+/++ video in RTP packets (RFC 4629) Wim Taymans <wim.taymans@gmail.com> @@ -591,7 +654,7 @@ rtph264depay RTP H264 depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts H264 video from RTP packets (RFC 3984) Wim Taymans <wim.taymans@gmail.com> @@ -612,7 +675,7 @@ rtph264pay RTP H264 payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encode H264 video into RTP packets (RFC 3984) Laurent Glayal <spglegle@yahoo.fr> @@ -633,7 +696,7 @@ rtpilbcdepay RTP iLBC depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts iLBC audio from RTP packets (RFC 3952) Philippe Kalaf <philippe.kalaf@collabora.co.uk> @@ -654,7 +717,7 @@ rtpilbcpay RTP iLBC Payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Packetize iLBC audio streams into RTP packets Philippe Kalaf <philippe.kalaf@collabora.co.uk> @@ -675,7 +738,7 @@ rtpj2kdepay RTP JPEG 2000 depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts JPEG 2000 video from RTP packets (RFC 5371) Wim Taymans <wim.taymans@gmail.com> @@ -696,7 +759,7 @@ rtpj2kpay RTP JPEG 2000 payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encodes JPEG 2000 pictures into RTP packets (RFC 5371) Wim Taymans <wim.taymans@gmail.com> @@ -717,7 +780,7 @@ rtpjpegdepay RTP JPEG depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts JPEG video from RTP packets (RFC 2435) Wim Taymans <wim.taymans@gmail.com> @@ -738,7 +801,7 @@ rtpjpegpay RTP JPEG payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encodes JPEG pictures into RTP packets (RFC 2435) Axis Communications <dev-gstreamer@axis.com> @@ -759,7 +822,7 @@ rtpmp1sdepay RTP MPEG1 System Stream depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts MPEG1 System Streams from RTP packets (RFC 3555) Wim Taymans <wim.taymans@gmail.com> @@ -780,7 +843,7 @@ rtpmp2tdepay RTP MPEG Transport Stream depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts MPEG2 TS from RTP packets (RFC 2250) Wim Taymans <wim.taymans@gmail.com>, Thijs Vermeir <thijs.vermeir@barco.com> @@ -801,7 +864,7 @@ rtpmp2tpay RTP MPEG2 Transport Stream payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encodes MPEG2 TS into RTP packets (RFC 2250) Wim Taymans <wim.taymans@gmail.com> @@ -822,7 +885,7 @@ rtpmp4adepay RTP MPEG4 audio depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts MPEG4 audio from RTP packets (RFC 3016) Nokia Corporation (contact <stefan.kost@nokia.com>), Wim Taymans <wim.taymans@gmail.com> @@ -843,7 +906,7 @@ rtpmp4apay RTP MPEG4 audio payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload MPEG4 audio as RTP packets (RFC 3016) Wim Taymans <wim.taymans@gmail.com> @@ -864,7 +927,7 @@ rtpmp4gdepay RTP MPEG4 ES depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts MPEG4 elementary streams from RTP packets (RFC 3640) Wim Taymans <wim.taymans@gmail.com> @@ -885,7 +948,7 @@ rtpmp4gpay RTP MPEG4 ES payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload MPEG4 elementary streams as RTP packets (RFC 3640) Wim Taymans <wim.taymans@gmail.com> @@ -906,7 +969,7 @@ rtpmp4vdepay RTP MPEG4 video depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts MPEG4 video from RTP packets (RFC 3016) Wim Taymans <wim.taymans@gmail.com> @@ -927,7 +990,7 @@ rtpmp4vpay RTP MPEG4 Video payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload MPEG-4 video as RTP packets (RFC 3016) Wim Taymans <wim.taymans@gmail.com> @@ -935,7 +998,7 @@ sink sink always -
video/mpeg, mpegversion=(int)4, systemstream=(boolean)false
+
video/mpeg, mpegversion=(int)4, systemstream=(boolean)false; video/x-xvid
src @@ -948,7 +1011,7 @@ rtpmpadepay RTP MPEG audio depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts MPEG audio from RTP packets (RFC 2038) Wim Taymans <wim.taymans@gmail.com> @@ -969,7 +1032,7 @@ rtpmpapay RTP MPEG audio payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload MPEG audio as RTP packets (RFC 2038) Wim Taymans <wim.taymans@gmail.com> @@ -990,7 +1053,7 @@ rtpmparobustdepay RTP MPEG audio depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts MPEG audio from RTP packets (RFC 5219) Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> @@ -1011,7 +1074,7 @@ rtpmpvdepay RTP MPEG video depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts MPEG video from RTP packets (RFC 2250) Wim Taymans <wim.taymans@gmail.com> @@ -1032,7 +1095,7 @@ rtpmpvpay RTP MPEG2 ES video payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encodes MPEG2 ES into RTP packets (RFC 2250) Thijs Vermeir <thijsvermeir@gmail.com> @@ -1053,7 +1116,7 @@ rtppcmadepay RTP PCMA depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts PCMA audio from RTP packets Edgard Lima <edgard.lima@indt.org.br>, Zeeshan Ali <zeenix@gmail.com> @@ -1074,7 +1137,7 @@ rtppcmapay RTP PCMA payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encodes PCMA audio into a RTP packet Edgard Lima <edgard.lima@indt.org.br> @@ -1095,7 +1158,7 @@ rtppcmudepay RTP PCMU depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts PCMU audio from RTP packets Edgard Lima <edgard.lima@indt.org.br>, Zeeshan Ali <zeenix@gmail.com> @@ -1116,7 +1179,7 @@ rtppcmupay RTP PCMU payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encodes PCMU audio into a RTP packet Edgard Lima <edgard.lima@indt.org.br> @@ -1137,7 +1200,7 @@ rtpqcelpdepay RTP QCELP depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts QCELP (PureVoice) audio from RTP packets (RFC 2658) Wim Taymans <wim.taymans@gmail.com> @@ -1158,7 +1221,7 @@ rtpqdm2depay RTP QDM2 depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts QDM2 audio from RTP packets (no RFC) Edward Hervey <bilboed@bilboed.com> @@ -1179,7 +1242,7 @@ rtpsirendepay RTP Siren packet depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts Siren audio from RTP packets Philippe Kalaf <philippe.kalaf@collabora.co.uk> @@ -1200,7 +1263,7 @@ rtpsirenpay RTP Payloader for Siren Audio - Codec/Payloader/Network + Codec/Payloader/Network/RTP Packetize Siren audio streams into RTP packets Youness Alaoui <kakaroto@kakaroto.homelinux.net> @@ -1221,7 +1284,7 @@ rtpspeexdepay RTP Speex depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts Speex audio from RTP packets Edgard Lima <edgard.lima@indt.org.br> @@ -1242,7 +1305,7 @@ rtpspeexpay RTP Speex payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encodes Speex audio into a RTP packet Edgard Lima <edgard.lima@indt.org.br> @@ -1263,7 +1326,7 @@ rtpsv3vdepay RTP SVQ3 depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts SVQ3 video from RTP packets (no RFC) Wim Taymans <wim.taymans@gmail.com> @@ -1284,7 +1347,7 @@ rtptheoradepay RTP Theora depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts Theora video from RTP packets (draft-01 of RFC XXXX) Wim Taymans <wim.taymans@gmail.com> @@ -1305,7 +1368,7 @@ rtptheorapay RTP Theora payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encode Theora video into RTP packets (draft-01 RFC XXXX) Wim Taymans <wim.taymans@gmail.com> @@ -1326,7 +1389,7 @@ rtpvorbisdepay RTP Vorbis depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts Vorbis Audio from RTP packets (RFC 5215) Wim Taymans <wim.taymans@gmail.com> @@ -1347,7 +1410,7 @@ rtpvorbispay RTP Vorbis depayloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload-encode Vorbis audio into RTP packets (RFC 5215) Wim Taymans <wimi.taymans@gmail.com> @@ -1368,7 +1431,7 @@ rtpvrawdepay RTP Raw Video depayloader - Codec/Depayloader/Network + Codec/Depayloader/Network/RTP Extracts raw video from RTP packets (RFC 4175) Wim Taymans <wim.taymans@gmail.com> @@ -1389,7 +1452,7 @@ rtpvrawpay RTP Raw Video payloader - Codec/Payloader/Network + Codec/Payloader/Network/RTP Payload raw video as RTP packets (RFC 4175) Wim Taymans <wim.taymans@gmail.com> diff --git a/docs/plugins/inspect/plugin-rtsp.xml b/docs/plugins/inspect/plugin-rtsp.xml index 621dc7a07b..c643cdd17a 100644 --- a/docs/plugins/inspect/plugin-rtsp.xml +++ b/docs/plugins/inspect/plugin-rtsp.xml @@ -3,7 +3,7 @@ transfer data via RTSP ../../gst/rtsp/.libs/libgstrtsp.so libgstrtsp.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-shapewipe.xml b/docs/plugins/inspect/plugin-shapewipe.xml index e8ea7b2abe..fdf823145e 100644 --- a/docs/plugins/inspect/plugin-shapewipe.xml +++ b/docs/plugins/inspect/plugin-shapewipe.xml @@ -3,7 +3,7 @@ Shape Wipe transition filter ../../gst/shapewipe/.libs/libgstshapewipe.so libgstshapewipe.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-shout2send.xml b/docs/plugins/inspect/plugin-shout2send.xml index b2c96942a5..3927945275 100644 --- a/docs/plugins/inspect/plugin-shout2send.xml +++ b/docs/plugins/inspect/plugin-shout2send.xml @@ -3,7 +3,7 @@ Sends data to an icecast server using libshout2 ../../ext/shout2/.libs/libgstshout2.so libgstshout2.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good libshout2 diff --git a/docs/plugins/inspect/plugin-smpte.xml b/docs/plugins/inspect/plugin-smpte.xml index f7c22d45ac..0d24a5afef 100644 --- a/docs/plugins/inspect/plugin-smpte.xml +++ b/docs/plugins/inspect/plugin-smpte.xml @@ -3,7 +3,7 @@ Apply the standard SMPTE transitions on video images ../../gst/smpte/.libs/libgstsmpte.so libgstsmpte.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-soup.xml b/docs/plugins/inspect/plugin-soup.xml index a9a826ce13..0cbc37192a 100644 --- a/docs/plugins/inspect/plugin-soup.xml +++ b/docs/plugins/inspect/plugin-soup.xml @@ -3,7 +3,7 @@ libsoup HTTP client src ../../ext/soup/.libs/libgstsouphttpsrc.so libgstsouphttpsrc.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-spectrum.xml b/docs/plugins/inspect/plugin-spectrum.xml index 7310f1226e..1df70e15f6 100644 --- a/docs/plugins/inspect/plugin-spectrum.xml +++ b/docs/plugins/inspect/plugin-spectrum.xml @@ -3,7 +3,7 @@ Run an FFT on the audio signal, output spectrum data ../../gst/spectrum/.libs/libgstspectrum.so libgstspectrum.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-speex.xml b/docs/plugins/inspect/plugin-speex.xml index c45f6dba50..959df3cad0 100644 --- a/docs/plugins/inspect/plugin-speex.xml +++ b/docs/plugins/inspect/plugin-speex.xml @@ -3,7 +3,7 @@ Speex plugin library ../../ext/speex/.libs/libgstspeex.so libgstspeex.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-taglib.xml b/docs/plugins/inspect/plugin-taglib.xml index 5e6eed40c3..271141b0b3 100644 --- a/docs/plugins/inspect/plugin-taglib.xml +++ b/docs/plugins/inspect/plugin-taglib.xml @@ -3,7 +3,7 @@ Tag writing plug-in based on taglib ../../ext/taglib/.libs/libgsttaglib.so libgsttaglib.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-udp.xml b/docs/plugins/inspect/plugin-udp.xml index 768d1b9249..8870867133 100644 --- a/docs/plugins/inspect/plugin-udp.xml +++ b/docs/plugins/inspect/plugin-udp.xml @@ -3,7 +3,7 @@ transfer data via UDP ../../gst/udp/.libs/libgstudp.so libgstudp.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-video4linux2.xml b/docs/plugins/inspect/plugin-video4linux2.xml index 598ae23c17..6d5fd2fddd 100644 --- a/docs/plugins/inspect/plugin-video4linux2.xml +++ b/docs/plugins/inspect/plugin-video4linux2.xml @@ -3,7 +3,7 @@ elements for Video 4 Linux ../../sys/v4l2/.libs/libgstvideo4linux2.so libgstvideo4linux2.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-videobox.xml b/docs/plugins/inspect/plugin-videobox.xml index 13c9bedd8f..121ff06ed2 100644 --- a/docs/plugins/inspect/plugin-videobox.xml +++ b/docs/plugins/inspect/plugin-videobox.xml @@ -3,7 +3,7 @@ resizes a video by adding borders or cropping ../../gst/videobox/.libs/libgstvideobox.so libgstvideobox.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-videocrop.xml b/docs/plugins/inspect/plugin-videocrop.xml index f87c2989e7..4460f5c1f4 100644 --- a/docs/plugins/inspect/plugin-videocrop.xml +++ b/docs/plugins/inspect/plugin-videocrop.xml @@ -3,7 +3,7 @@ Crops video into a user-defined region ../../gst/videocrop/.libs/libgstvideocrop.so libgstvideocrop.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-videofilter.xml b/docs/plugins/inspect/plugin-videofilter.xml index fcd028ca47..5325b1d56d 100644 --- a/docs/plugins/inspect/plugin-videofilter.xml +++ b/docs/plugins/inspect/plugin-videofilter.xml @@ -3,7 +3,7 @@ Video filters plugin ../../gst/videofilter/.libs/libgstvideofilter.so libgstvideofilter.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-videomixer.xml b/docs/plugins/inspect/plugin-videomixer.xml index c1b460d3b0..49b1bfaa67 100644 --- a/docs/plugins/inspect/plugin-videomixer.xml +++ b/docs/plugins/inspect/plugin-videomixer.xml @@ -3,7 +3,7 @@ Video mixer ../../gst/videomixer/.libs/libgstvideomixer.so libgstvideomixer.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-wavenc.xml b/docs/plugins/inspect/plugin-wavenc.xml index bb3c6125ec..f021a046ae 100644 --- a/docs/plugins/inspect/plugin-wavenc.xml +++ b/docs/plugins/inspect/plugin-wavenc.xml @@ -3,7 +3,7 @@ Encode raw audio into WAV ../../gst/wavenc/.libs/libgstwavenc.so libgstwavenc.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-wavpack.xml b/docs/plugins/inspect/plugin-wavpack.xml index 987e6b96b7..89f3d67343 100644 --- a/docs/plugins/inspect/plugin-wavpack.xml +++ b/docs/plugins/inspect/plugin-wavpack.xml @@ -3,7 +3,7 @@ Wavpack lossless/lossy audio format handling ../../ext/wavpack/.libs/libgstwavpack.so libgstwavpack.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-wavparse.xml b/docs/plugins/inspect/plugin-wavparse.xml index b05e4eb86d..c47fab1f7d 100644 --- a/docs/plugins/inspect/plugin-wavparse.xml +++ b/docs/plugins/inspect/plugin-wavparse.xml @@ -3,7 +3,7 @@ Parse a .wav file into raw audio ../../gst/wavparse/.libs/libgstwavparse.so libgstwavparse.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-ximagesrc.xml b/docs/plugins/inspect/plugin-ximagesrc.xml index 8173a1b22a..9e575bd9e5 100644 --- a/docs/plugins/inspect/plugin-ximagesrc.xml +++ b/docs/plugins/inspect/plugin-ximagesrc.xml @@ -3,7 +3,7 @@ X11 video input plugin using standard Xlib calls ../../sys/ximage/.libs/libgstximagesrc.so libgstximagesrc.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/docs/plugins/inspect/plugin-y4menc.xml b/docs/plugins/inspect/plugin-y4menc.xml index f48e39cd76..b988cfd944 100644 --- a/docs/plugins/inspect/plugin-y4menc.xml +++ b/docs/plugins/inspect/plugin-y4menc.xml @@ -3,7 +3,7 @@ Encodes a YUV frame into the yuv4mpeg format (mjpegtools) ../../gst/y4m/.libs/libgsty4menc.so libgsty4menc.so - 0.10.26.1 + 0.10.27.1 LGPL gst-plugins-good GStreamer Good Plug-ins git diff --git a/ext/Makefile.am b/ext/Makefile.am index 1dc2e2abca..409408ea8f 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -46,6 +46,12 @@ else HAL_DIR = endif +if USE_JACK +JACK_DIR=jack +else +JACK_DIR= +endif + if USE_JPEG JPEG_DIR = jpeg else @@ -135,6 +141,7 @@ SUBDIRS = \ $(GCONF_DIR) \ $(GDK_PIXBUF_DIR) \ $(HAL_DIR) \ + $(JACK_DIR) \ $(JPEG_DIR) \ $(LIBCACA_DIR) \ $(LIBDV_DIR) \ @@ -158,6 +165,7 @@ DIST_SUBDIRS = \ gconf \ gdk_pixbuf \ hal \ + jack \ jpeg \ libcaca \ libpng \ diff --git a/ext/cairo/gstcairorender.c b/ext/cairo/gstcairorender.c index 827ef3d26b..cf1b563aa8 100644 --- a/ext/cairo/gstcairorender.c +++ b/ext/cairo/gstcairorender.c @@ -264,52 +264,63 @@ gst_cairo_render_setcaps_sink (GstPad * pad, GstCaps * caps) return TRUE; } -static GstStaticPadTemplate t_src = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ( + +#define SIZE_CAPS "width = (int) [ 1, MAX], height = (int) [ 1, MAX] " #if CAIRO_HAS_PDF_SURFACE - "application/pdf, " - "width = (int) [ 1, MAX], " "height = (int) [ 1, MAX] " +#define PDF_CAPS "application/pdf, " SIZE_CAPS +#else +#define PDF_CAPS #endif #if CAIRO_HAS_PDF_SURFACE && (CAIRO_HAS_PS_SURFACE || CAIRO_HAS_SVG_SURFACE || CAIRO_HAS_PNG_FUNCTIONS) - ";" +#define JOIN1 ";" +#else +#define JOIN1 #endif #if CAIRO_HAS_PS_SURFACE - "application/postscript, " - "width = (int) [ 1, MAX], " "height = (int) [ 1, MAX] " +#define PS_CAPS "application/postscript, " SIZE_CAPS +#else +#define PS_CAPS #endif #if (CAIRO_HAS_PDF_SURFACE || CAIRO_HAS_PS_SURFACE) && (CAIRO_HAS_SVG_SURFACE || CAIRO_HAS_PNG_FUNCTIONS) - ";" +#define JOIN2 ";" +#else +#define JOIN2 #endif #if CAIRO_HAS_SVG_SURFACE - "image/svg+xml, " - "width = (int) [ 1, MAX], " "height = (int) [ 1, MAX] " +#define SVG_CAPS "image/svg+xml, " SIZE_CAPS +#else +#define SVG_CAPS #endif #if (CAIRO_HAS_PDF_SURFACE || CAIRO_HAS_PS_SURFACE || CAIRO_HAS_SVG_SURFACE) && CAIRO_HAS_PNG_FUNCTIONS - ";" +#define JOIN3 ";" +#else +#define JOIN3 #endif #if CAIRO_HAS_PNG_FUNCTIONS - "image/png, " "width = (int) [ 1, MAX], " "height = (int) [ 1, MAX] " -#endif - )); -static GstStaticPadTemplate t_snk = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ( -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - GST_VIDEO_CAPS_BGRx " ; " GST_VIDEO_CAPS_BGRA " ; " +#define PNG_CAPS "image/png, " SIZE_CAPS +#define PNG_CAPS2 "; image/png, " SIZE_CAPS #else - GST_VIDEO_CAPS_xRGB " ; " GST_VIDEO_CAPS_ARGB " ; " +#define PNG_CAPS +#define PNG_CAPS2 #endif + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN +#define ARGB_CAPS GST_VIDEO_CAPS_BGRx " ; " GST_VIDEO_CAPS_BGRA " ; " +#else +#define ARGB_CAPS GST_VIDEO_CAPS_xRGB " ; " GST_VIDEO_CAPS_ARGB " ; " +#endif +static GstStaticPadTemplate t_src = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS (PDF_CAPS JOIN1 PS_CAPS JOIN2 SVG_CAPS JOIN3 PNG_CAPS)); +static GstStaticPadTemplate t_snk = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS (ARGB_CAPS GST_VIDEO_CAPS_YUV ("Y800") " ; " "video/x-raw-gray, " "bpp = 8, " "depth = 8, " "width = " GST_VIDEO_SIZE_RANGE ", " "height = " GST_VIDEO_SIZE_RANGE ", " "framerate = " GST_VIDEO_FPS_RANGE - " ; " -#if CAIRO_HAS_PNG_FUNCTIONS - "image/png, " - "width = " GST_VIDEO_SIZE_RANGE ", " "height = " GST_VIDEO_SIZE_RANGE -#endif - )); + PNG_CAPS2)); GST_BOILERPLATE (GstCairoRender, gst_cairo_render, GstElement, GST_TYPE_ELEMENT); diff --git a/ext/cairo/gsttimeoverlay.c b/ext/cairo/gsttimeoverlay.c index 18f18f8fe5..b731c37894 100644 --- a/ext/cairo/gsttimeoverlay.c +++ b/ext/cairo/gsttimeoverlay.c @@ -36,19 +36,16 @@ #include "config.h" #endif +#include + #include #include -#include #include #include -#ifndef HAVE_RINT -#define rint(x) ((double) floor((x)+(((x) < 0)? -0.5 : 0.5))) -#endif - static GstStaticPadTemplate gst_cairo_time_overlay_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, diff --git a/ext/dv/gstdvdemux.c b/ext/dv/gstdvdemux.c index 5261d479f8..c1ecd9261c 100644 --- a/ext/dv/gstdvdemux.c +++ b/ext/dv/gstdvdemux.c @@ -895,17 +895,13 @@ gst_dvdemux_convert_segment (GstDVDemux * dvdemux, GstSegment * src, * * Convert the time seek to a bytes seek and send it * upstream - * - * FIXME, upstream might be able to perform time based - * seek too. - * * Does not take ownership of the event. */ static gboolean gst_dvdemux_handle_push_seek (GstDVDemux * dvdemux, GstPad * pad, GstEvent * event) { - gboolean res; + gboolean res = FALSE; gdouble rate; GstSeekFlags flags; GstFormat format; @@ -917,19 +913,24 @@ gst_dvdemux_handle_push_seek (GstDVDemux * dvdemux, GstPad * pad, gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); - /* we convert the start/stop on the srcpad to the byte format - * on the sinkpad and forward the event */ - res = gst_dvdemux_convert_src_to_sink (dvdemux, pad, - format, cur, stop, GST_FORMAT_BYTES, &start_position, &end_position); - if (!res) - goto done; + /* First try if upstream can handle time based seeks */ + if (format == GST_FORMAT_TIME) + res = gst_pad_push_event (dvdemux->sinkpad, event); - /* now this is the updated seek event on bytes */ - newevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, - cur_type, start_position, stop_type, end_position); + if (!res) { + /* we convert the start/stop on the srcpad to the byte format + * on the sinkpad and forward the event */ + res = gst_dvdemux_convert_src_to_sink (dvdemux, pad, + format, cur, stop, GST_FORMAT_BYTES, &start_position, &end_position); + if (!res) + goto done; - res = gst_pad_push_event (dvdemux->sinkpad, newevent); + /* now this is the updated seek event on bytes */ + newevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, + cur_type, start_position, stop_type, end_position); + res = gst_pad_push_event (dvdemux->sinkpad, newevent); + } done: return res; } diff --git a/ext/jack/.gitignore b/ext/jack/.gitignore new file mode 100644 index 0000000000..916f265c7a --- /dev/null +++ b/ext/jack/.gitignore @@ -0,0 +1 @@ +*.loT diff --git a/ext/jack/Makefile.am b/ext/jack/Makefile.am new file mode 100644 index 0000000000..cf7789971a --- /dev/null +++ b/ext/jack/Makefile.am @@ -0,0 +1,10 @@ + +plugin_LTLIBRARIES = libgstjack.la + +libgstjack_la_SOURCES = gstjackutil.c gstjack.c gstjackaudiosrc.c gstjackaudiosink.c gstjackaudioclient.c +libgstjack_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(JACK_CFLAGS) +libgstjack_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) $(JACK_LIBS) +libgstjack_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstjack_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = gstjackutil.h gstjackaudiosrc.h gstjackaudiosink.h gstjackaudioclient.h gstjack.h gstjackringbuffer.h diff --git a/ext/jack/gstjack.c b/ext/jack/gstjack.c new file mode 100644 index 0000000000..8180afbb3c --- /dev/null +++ b/ext/jack/gstjack.c @@ -0,0 +1,97 @@ +/* GStreamer Jack plugins + * Copyright (C) 2006 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstjackaudiosrc.h" +#include "gstjackaudiosink.h" + +GType +gst_jack_connect_get_type (void) +{ + static volatile gsize jack_connect_type = 0; + + if (g_once_init_enter (&jack_connect_type)) { + static const GEnumValue jack_connect_enums[] = { + {GST_JACK_CONNECT_NONE, + "Don't automatically connect ports to physical ports", "none"}, + {GST_JACK_CONNECT_AUTO, + "Automatically connect ports to physical ports", "auto"}, + {GST_JACK_CONNECT_AUTO_FORCED, + "Automatically connect ports to as many physical ports as possible", + "auto-forced"}, + {0, NULL, NULL}, + }; + GType tmp = g_enum_register_static ("GstJackConnect", jack_connect_enums); + g_once_init_leave (&jack_connect_type, tmp); + } + return (GType) jack_connect_type; +} + + +static gpointer +gst_jack_client_copy (gpointer jclient) +{ + return jclient; +} + + +static void +gst_jack_client_free (gpointer jclient) +{ + return; +} + + +GType +gst_jack_client_get_type (void) +{ + static volatile gsize jack_client_type = 0; + + if (g_once_init_enter (&jack_client_type)) { + /* hackish, but makes it show up nicely in gst-inspect */ + GType tmp = g_boxed_type_register_static ("JackClient", + (GBoxedCopyFunc) gst_jack_client_copy, + (GBoxedFreeFunc) gst_jack_client_free); + g_once_init_leave (&jack_client_type, tmp); + } + + return (GType) jack_client_type; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_element_register (plugin, "jackaudiosrc", GST_RANK_PRIMARY, + GST_TYPE_JACK_AUDIO_SRC)) + return FALSE; + if (!gst_element_register (plugin, "jackaudiosink", GST_RANK_PRIMARY, + GST_TYPE_JACK_AUDIO_SINK)) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "jack", + "JACK audio elements", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/jack/gstjack.h b/ext/jack/gstjack.h new file mode 100644 index 0000000000..d923866dfa --- /dev/null +++ b/ext/jack/gstjack.h @@ -0,0 +1,55 @@ +/* GStreamer + * Copyright (C) 2006 Wim Taymans + * + * gstjack.h: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _GST_JACK_H_ +#define _GST_JACK_H_ + + +/** + * GstJackConnect: + * @GST_JACK_CONNECT_NONE: Don't automatically connect to physical ports. + * In this mode, the element will accept any number of input channels and will + * create (but not connect) an output port for each channel. + * @GST_JACK_CONNECT_AUTO: In this mode, the element will try to connect each + * output port to a random physical jack input pin. The sink will + * expose the number of physical channels on its pad caps. + * @GST_JACK_CONNECT_AUTO_FORCED: In this mode, the element will try to connect each + * output port to a random physical jack input pin. The element will accept any number + * of input channels. + * + * Specify how the output ports will be connected. + */ + +typedef enum { + GST_JACK_CONNECT_NONE, + GST_JACK_CONNECT_AUTO, + GST_JACK_CONNECT_AUTO_FORCED +} GstJackConnect; + +typedef jack_default_audio_sample_t sample_t; + +#define GST_TYPE_JACK_CONNECT (gst_jack_connect_get_type()) +#define GST_TYPE_JACK_CLIENT (gst_jack_client_get_type ()) + +GType gst_jack_client_get_type(void); +GType gst_jack_connect_get_type(void); + +#endif // _GST_JACK_H_ diff --git a/ext/jack/gstjackaudioclient.c b/ext/jack/gstjackaudioclient.c new file mode 100644 index 0000000000..1789edb60d --- /dev/null +++ b/ext/jack/gstjackaudioclient.c @@ -0,0 +1,525 @@ +/* GStreamer + * Copyright (C) 2006 Wim Taymans + * + * gstjackaudioclient.c: jack audio client implementation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include "gstjackaudioclient.h" + +GST_DEBUG_CATEGORY_STATIC (gst_jack_audio_client_debug); +#define GST_CAT_DEFAULT gst_jack_audio_client_debug + +void +gst_jack_audio_client_init (void) +{ + GST_DEBUG_CATEGORY_INIT (gst_jack_audio_client_debug, "jackclient", 0, + "jackclient helpers"); +} + +/* a list of global connections indexed by id and server. */ +G_LOCK_DEFINE_STATIC (connections_lock); +static GList *connections; + +/* the connection to a server */ +typedef struct +{ + gint refcount; + GMutex *lock; + GCond *flush_cond; + + /* id/server pair and the connection */ + gchar *id; + gchar *server; + jack_client_t *client; + + /* lists of GstJackAudioClients */ + gint n_clients; + GList *src_clients; + GList *sink_clients; +} GstJackAudioConnection; + +/* an object sharing a jack_client_t connection. */ +struct _GstJackAudioClient +{ + GstJackAudioConnection *conn; + + GstJackClientType type; + gboolean active; + gboolean deactivate; + + void (*shutdown) (void *arg); + JackProcessCallback process; + JackBufferSizeCallback buffer_size; + JackSampleRateCallback sample_rate; + gpointer user_data; +}; + +typedef jack_default_audio_sample_t sample_t; + +typedef struct +{ + jack_nframes_t nframes; + gpointer user_data; +} JackCB; + +static int +jack_process_cb (jack_nframes_t nframes, void *arg) +{ + GstJackAudioConnection *conn = (GstJackAudioConnection *) arg; + GList *walk; + int res = 0; + + g_mutex_lock (conn->lock); + /* call sources first, then sinks. Sources will either push data into the + * ringbuffer of the sinks, which will then pull the data out of it, or + * sinks will pull the data from the sources. */ + for (walk = conn->src_clients; walk; walk = g_list_next (walk)) { + GstJackAudioClient *client = (GstJackAudioClient *) walk->data; + + /* only call active clients */ + if ((client->active || client->deactivate) && client->process) { + res = client->process (nframes, client->user_data); + if (client->deactivate) { + client->deactivate = FALSE; + g_cond_signal (conn->flush_cond); + } + } + } + for (walk = conn->sink_clients; walk; walk = g_list_next (walk)) { + GstJackAudioClient *client = (GstJackAudioClient *) walk->data; + + /* only call active clients */ + if ((client->active || client->deactivate) && client->process) { + res = client->process (nframes, client->user_data); + if (client->deactivate) { + client->deactivate = FALSE; + g_cond_signal (conn->flush_cond); + } + } + } + g_mutex_unlock (conn->lock); + + return res; +} + +/* we error out */ +static int +jack_sample_rate_cb (jack_nframes_t nframes, void *arg) +{ + return 0; +} + +/* we error out */ +static int +jack_buffer_size_cb (jack_nframes_t nframes, void *arg) +{ + return 0; +} + +static void +jack_shutdown_cb (void *arg) +{ + GstJackAudioConnection *conn = (GstJackAudioConnection *) arg; + GList *walk; + + GST_DEBUG ("disconnect client %s from server %s", conn->id, + GST_STR_NULL (conn->server)); + + g_mutex_lock (conn->lock); + for (walk = conn->src_clients; walk; walk = g_list_next (walk)) { + GstJackAudioClient *client = (GstJackAudioClient *) walk->data; + + if (client->shutdown) + client->shutdown (client->user_data); + } + for (walk = conn->sink_clients; walk; walk = g_list_next (walk)) { + GstJackAudioClient *client = (GstJackAudioClient *) walk->data; + + if (client->shutdown) + client->shutdown (client->user_data); + } + g_mutex_unlock (conn->lock); +} + +typedef struct +{ + const gchar *id; + const gchar *server; +} FindData; + +static gint +connection_find (GstJackAudioConnection * conn, FindData * data) +{ + /* id's must match */ + if (strcmp (conn->id, data->id)) + return 1; + + /* both the same or NULL */ + if (conn->server == data->server) + return 0; + + /* we cannot compare NULL */ + if (conn->server == NULL || data->server == NULL) + return 1; + + if (strcmp (conn->server, data->server)) + return 1; + + return 0; +} + +/* make a connection with @id and @server. Returns NULL on failure with the + * status set. */ +static GstJackAudioConnection * +gst_jack_audio_make_connection (const gchar * id, const gchar * server, + jack_client_t * jclient, jack_status_t * status) +{ + GstJackAudioConnection *conn; + jack_options_t options; + gint res; + + *status = 0; + + GST_DEBUG ("new client %s, connecting to server %s", id, + GST_STR_NULL (server)); + + /* never start a server */ + options = JackNoStartServer; + /* if we have a servername, use it */ + if (server != NULL) + options |= JackServerName; + /* open the client */ + if (jclient == NULL) + jclient = jack_client_open (id, options, status, server); + if (jclient == NULL) + goto could_not_open; + + /* now create object */ + conn = g_new (GstJackAudioConnection, 1); + conn->refcount = 1; + conn->lock = g_mutex_new (); + conn->flush_cond = g_cond_new (); + conn->id = g_strdup (id); + conn->server = g_strdup (server); + conn->client = jclient; + conn->n_clients = 0; + conn->src_clients = NULL; + conn->sink_clients = NULL; + + /* set our callbacks */ + jack_set_process_callback (jclient, jack_process_cb, conn); + /* these callbacks cause us to error */ + jack_set_buffer_size_callback (jclient, jack_buffer_size_cb, conn); + jack_set_sample_rate_callback (jclient, jack_sample_rate_cb, conn); + jack_on_shutdown (jclient, jack_shutdown_cb, conn); + + /* all callbacks are set, activate the client */ + if ((res = jack_activate (jclient))) + goto could_not_activate; + + GST_DEBUG ("opened connection %p", conn); + + return conn; + + /* ERRORS */ +could_not_open: + { + GST_DEBUG ("failed to open jack client, %d", *status); + return NULL; + } +could_not_activate: + { + GST_ERROR ("Could not activate client (%d)", res); + *status = JackFailure; + g_mutex_free (conn->lock); + g_free (conn->id); + g_free (conn->server); + g_free (conn); + return NULL; + } +} + +static GstJackAudioConnection * +gst_jack_audio_get_connection (const gchar * id, const gchar * server, + jack_client_t * jclient, jack_status_t * status) +{ + GstJackAudioConnection *conn; + GList *found; + FindData data; + + GST_DEBUG ("getting connection for id %s, server %s", id, + GST_STR_NULL (server)); + + data.id = id; + data.server = server; + + G_LOCK (connections_lock); + found = + g_list_find_custom (connections, &data, (GCompareFunc) connection_find); + if (found != NULL && jclient != NULL) { + /* we found it, increase refcount and return it */ + conn = (GstJackAudioConnection *) found->data; + conn->refcount++; + + GST_DEBUG ("found connection %p", conn); + } else { + /* make new connection */ + conn = gst_jack_audio_make_connection (id, server, jclient, status); + if (conn != NULL) { + GST_DEBUG ("created connection %p", conn); + /* add to list on success */ + connections = g_list_prepend (connections, conn); + } else { + GST_WARNING ("could not create connection"); + } + } + G_UNLOCK (connections_lock); + + return conn; +} + +static void +gst_jack_audio_unref_connection (GstJackAudioConnection * conn) +{ + gint res; + gboolean zero; + + GST_DEBUG ("unref connection %p refcnt %d", conn, conn->refcount); + + G_LOCK (connections_lock); + conn->refcount--; + if ((zero = (conn->refcount == 0))) { + GST_DEBUG ("closing connection %p", conn); + /* remove from list, we can release the mutex after removing the connection + * from the list because after that, nobody can access the connection anymore. */ + connections = g_list_remove (connections, conn); + } + G_UNLOCK (connections_lock); + + /* if we are zero, close and cleanup the connection */ + if (zero) { + /* don't use conn->lock here. two reasons: + * + * 1) its not necessary: jack_deactivate() will not return until the JACK thread + * associated with this connection is cleaned up by a thread join, hence + * no more callbacks can occur or be in progress. + * + * 2) it would deadlock anyway, because jack_deactivate() will sleep + * waiting for the JACK thread, and can thus cause deadlock in + * jack_process_cb() + */ + if ((res = jack_deactivate (conn->client))) { + /* we only warn, this means the server is probably shut down and the client + * is gone anyway. */ + GST_WARNING ("Could not deactivate Jack client (%d)", res); + } + /* close connection */ + if ((res = jack_client_close (conn->client))) { + /* we assume the client is gone. */ + GST_WARNING ("close failed (%d)", res); + } + + /* free resources */ + g_mutex_free (conn->lock); + g_cond_free (conn->flush_cond); + g_free (conn->id); + g_free (conn->server); + g_free (conn); + } +} + +static void +gst_jack_audio_connection_add_client (GstJackAudioConnection * conn, + GstJackAudioClient * client) +{ + g_mutex_lock (conn->lock); + switch (client->type) { + case GST_JACK_CLIENT_SOURCE: + conn->src_clients = g_list_append (conn->src_clients, client); + conn->n_clients++; + break; + case GST_JACK_CLIENT_SINK: + conn->sink_clients = g_list_append (conn->sink_clients, client); + conn->n_clients++; + break; + default: + g_warning ("trying to add unknown client type"); + break; + } + g_mutex_unlock (conn->lock); +} + +static void +gst_jack_audio_connection_remove_client (GstJackAudioConnection * conn, + GstJackAudioClient * client) +{ + g_mutex_lock (conn->lock); + switch (client->type) { + case GST_JACK_CLIENT_SOURCE: + conn->src_clients = g_list_remove (conn->src_clients, client); + conn->n_clients--; + break; + case GST_JACK_CLIENT_SINK: + conn->sink_clients = g_list_remove (conn->sink_clients, client); + conn->n_clients--; + break; + default: + g_warning ("trying to remove unknown client type"); + break; + } + g_mutex_unlock (conn->lock); +} + +/** + * gst_jack_audio_client_get: + * @id: the client id + * @server: the server to connect to or NULL for the default server + * @type: the client type + * @shutdown: a callback when the jack server shuts down + * @process: a callback when samples are available + * @buffer_size: a callback when the buffer_size changes + * @sample_rate: a callback when the sample_rate changes + * @user_data: user data passed to the callbacks + * @status: pointer to hold the jack status code in case of errors + * + * Get the jack client connection for @id and @server. Connections to the same + * @id and @server will receive the same physical Jack client connection and + * will therefore be scheduled in the same process callback. + * + * Returns: a #GstJackAudioClient. + */ +GstJackAudioClient * +gst_jack_audio_client_new (const gchar * id, const gchar * server, + jack_client_t * jclient, GstJackClientType type, + void (*shutdown) (void *arg), JackProcessCallback process, + JackBufferSizeCallback buffer_size, JackSampleRateCallback sample_rate, + gpointer user_data, jack_status_t * status) +{ + GstJackAudioClient *client; + GstJackAudioConnection *conn; + + g_return_val_if_fail (id != NULL, NULL); + g_return_val_if_fail (status != NULL, NULL); + + /* first get a connection for the id/server pair */ + conn = gst_jack_audio_get_connection (id, server, jclient, status); + if (conn == NULL) + goto no_connection; + + GST_INFO ("new client %s", id); + + /* make new client using the connection */ + client = g_new (GstJackAudioClient, 1); + client->active = client->deactivate = FALSE; + client->conn = conn; + client->type = type; + client->shutdown = shutdown; + client->process = process; + client->buffer_size = buffer_size; + client->sample_rate = sample_rate; + client->user_data = user_data; + + /* add the client to the connection */ + gst_jack_audio_connection_add_client (conn, client); + + return client; + + /* ERRORS */ +no_connection: + { + GST_DEBUG ("Could not get server connection (%d)", *status); + return NULL; + } +} + +/** + * gst_jack_audio_client_free: + * @client: a #GstJackAudioClient + * + * Free the resources used by @client. + */ +void +gst_jack_audio_client_free (GstJackAudioClient * client) +{ + GstJackAudioConnection *conn; + + g_return_if_fail (client != NULL); + + GST_INFO ("free client"); + + conn = client->conn; + + /* remove from connection first so that it's not scheduled anymore after this + * call */ + gst_jack_audio_connection_remove_client (conn, client); + gst_jack_audio_unref_connection (conn); + + g_free (client); +} + +/** + * gst_jack_audio_client_get_client: + * @client: a #GstJackAudioClient + * + * Get the jack audio client for @client. This function is used to perform + * operations on the jack server from this client. + * + * Returns: The jack audio client. + */ +jack_client_t * +gst_jack_audio_client_get_client (GstJackAudioClient * client) +{ + g_return_val_if_fail (client != NULL, NULL); + + /* no lock needed, the connection and the client does not change + * once the client is created. */ + return client->conn->client; +} + +/** + * gst_jack_audio_client_set_active: + * @client: a #GstJackAudioClient + * @active: new mode for the client + * + * Activate or deactive @client. When a client is activated it will receive + * callbacks when data should be processed. + * + * Returns: 0 if all ok. + */ +gint +gst_jack_audio_client_set_active (GstJackAudioClient * client, gboolean active) +{ + g_return_val_if_fail (client != NULL, -1); + + /* make sure that we are not dispatching the client */ + g_mutex_lock (client->conn->lock); + if (client->active && !active) { + /* we need to process once more to flush the port */ + client->deactivate = TRUE; + + /* need to wait for process_cb run once more */ + while (client->deactivate) + g_cond_wait (client->conn->flush_cond, client->conn->lock); + } + client->active = active; + g_mutex_unlock (client->conn->lock); + + return 0; +} diff --git a/ext/jack/gstjackaudioclient.h b/ext/jack/gstjackaudioclient.h new file mode 100644 index 0000000000..5fb7e35444 --- /dev/null +++ b/ext/jack/gstjackaudioclient.h @@ -0,0 +1,59 @@ +/* GStreamer + * Copyright (C) 2006 Wim Taymans + * + * gstjackaudioclient.h: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_JACK_AUDIO_CLIENT_H__ +#define __GST_JACK_AUDIO_CLIENT_H__ + +#include + +#include + +G_BEGIN_DECLS + +typedef enum +{ + GST_JACK_CLIENT_SOURCE, + GST_JACK_CLIENT_SINK +} GstJackClientType; + +typedef struct _GstJackAudioClient GstJackAudioClient; + +void gst_jack_audio_client_init (void); + + +GstJackAudioClient * gst_jack_audio_client_new (const gchar *id, const gchar *server, + jack_client_t *jclient, + GstJackClientType type, + void (*shutdown) (void *arg), + JackProcessCallback process, + JackBufferSizeCallback buffer_size, + JackSampleRateCallback sample_rate, + gpointer user_data, + jack_status_t *status); +void gst_jack_audio_client_free (GstJackAudioClient *client); + +jack_client_t * gst_jack_audio_client_get_client (GstJackAudioClient *client); + +gboolean gst_jack_audio_client_set_active (GstJackAudioClient *client, gboolean active); + +G_END_DECLS + +#endif /* __GST_JACK_AUDIO_CLIENT_H__ */ diff --git a/ext/jack/gstjackaudiosink.c b/ext/jack/gstjackaudiosink.c new file mode 100644 index 0000000000..4620bce712 --- /dev/null +++ b/ext/jack/gstjackaudiosink.c @@ -0,0 +1,853 @@ +/* GStreamer + * Copyright (C) 2006 Wim Taymans + * + * gstjackaudiosink.c: jack audio sink implementation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-jackaudiosink + * @see_also: #GstBaseAudioSink, #GstRingBuffer + * + * A Sink that outputs data to Jack ports. + * + * It will create N Jack ports named out_<name>_<num> where + * <name> is the element name and <num> is starting from 1. + * Each port corresponds to a gstreamer channel. + * + * The samplerate as exposed on the caps is always the same as the samplerate of + * the jack server. + * + * When the #GstJackAudioSink:connect property is set to auto, this element + * will try to connect each output port to a random physical jack input pin. In + * this mode, the sink will expose the number of physical channels on its pad + * caps. + * + * When the #GstJackAudioSink:connect property is set to none, the element will + * accept any number of input channels and will create (but not connect) an + * output port for each channel. + * + * The element will generate an error when the Jack server is shut down when it + * was PAUSED or PLAYING. This element does not support dynamic rate and buffer + * size changes at runtime. + * + * + * Example launch line + * |[ + * gst-launch audiotestsrc ! jackaudiosink + * ]| Play a sine wave to using jack. + * + * + * Last reviewed on 2006-11-30 (0.10.4) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "gstjackaudiosink.h" +#include "gstjackringbuffer.h" + +GST_DEBUG_CATEGORY_STATIC (gst_jack_audio_sink_debug); +#define GST_CAT_DEFAULT gst_jack_audio_sink_debug + +static gboolean +gst_jack_audio_sink_allocate_channels (GstJackAudioSink * sink, gint channels) +{ + jack_client_t *client; + + client = gst_jack_audio_client_get_client (sink->client); + + /* remove ports we don't need */ + while (sink->port_count > channels) { + jack_port_unregister (client, sink->ports[--sink->port_count]); + } + + /* alloc enough output ports */ + sink->ports = g_realloc (sink->ports, sizeof (jack_port_t *) * channels); + + /* create an output port for each channel */ + while (sink->port_count < channels) { + gchar *name; + + /* port names start from 1 and are local to the element */ + name = + g_strdup_printf ("out_%s_%d", GST_ELEMENT_NAME (sink), + sink->port_count + 1); + sink->ports[sink->port_count] = + jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + if (sink->ports[sink->port_count] == NULL) + return FALSE; + + sink->port_count++; + + g_free (name); + } + return TRUE; +} + +static void +gst_jack_audio_sink_free_channels (GstJackAudioSink * sink) +{ + gint res, i = 0; + jack_client_t *client; + + client = gst_jack_audio_client_get_client (sink->client); + + /* get rid of all ports */ + while (sink->port_count) { + GST_LOG_OBJECT (sink, "unregister port %d", i); + if ((res = jack_port_unregister (client, sink->ports[i++]))) { + GST_DEBUG_OBJECT (sink, "unregister of port failed (%d)", res); + } + sink->port_count--; + } + g_free (sink->ports); + sink->ports = NULL; +} + +/* ringbuffer abstract base class */ +static GType +gst_jack_ring_buffer_get_type (void) +{ + static volatile gsize ringbuffer_type = 0; + + if (g_once_init_enter (&ringbuffer_type)) { + static const GTypeInfo ringbuffer_info = { + sizeof (GstJackRingBufferClass), + NULL, + NULL, + (GClassInitFunc) gst_jack_ring_buffer_class_init, + NULL, + NULL, + sizeof (GstJackRingBuffer), + 0, + (GInstanceInitFunc) gst_jack_ring_buffer_init, + NULL + }; + GType tmp = g_type_register_static (GST_TYPE_RING_BUFFER, + "GstJackAudioSinkRingBuffer", &ringbuffer_info, 0); + g_once_init_leave (&ringbuffer_type, tmp); + } + + return (GType) ringbuffer_type; +} + +static void +gst_jack_ring_buffer_class_init (GstJackRingBufferClass * klass) +{ + GObjectClass *gobject_class; + GstObjectClass *gstobject_class; + GstRingBufferClass *gstringbuffer_class; + + gobject_class = (GObjectClass *) klass; + gstobject_class = (GstObjectClass *) klass; + gstringbuffer_class = (GstRingBufferClass *) klass; + + ring_parent_class = g_type_class_peek_parent (klass); + + gstringbuffer_class->open_device = + GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_open_device); + gstringbuffer_class->close_device = + GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_close_device); + gstringbuffer_class->acquire = + GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_acquire); + gstringbuffer_class->release = + GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_release); + gstringbuffer_class->start = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_start); + gstringbuffer_class->pause = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_pause); + gstringbuffer_class->resume = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_start); + gstringbuffer_class->stop = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_stop); + + gstringbuffer_class->delay = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_delay); +} + +/* this is the callback of jack. This should RT-safe. + */ +static int +jack_process_cb (jack_nframes_t nframes, void *arg) +{ + GstJackAudioSink *sink; + GstRingBuffer *buf; + GstJackRingBuffer *abuf; + gint readseg, len; + guint8 *readptr; + gint i, j, flen, channels; + sample_t **buffers, *data; + + buf = GST_RING_BUFFER_CAST (arg); + abuf = GST_JACK_RING_BUFFER_CAST (arg); + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + + channels = buf->spec.channels; + + /* alloc pointers to samples */ + buffers = g_alloca (sizeof (sample_t *) * channels); + + /* get target buffers */ + for (i = 0; i < channels; i++) { + buffers[i] = (sample_t *) jack_port_get_buffer (sink->ports[i], nframes); + } + + if (gst_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) { + flen = len / channels; + + /* the number of samples must be exactly the segment size */ + if (nframes * sizeof (sample_t) != flen) + goto wrong_size; + + GST_DEBUG_OBJECT (sink, "copy %d frames: %p, %d bytes, %d channels", + nframes, readptr, flen, channels); + data = (sample_t *) readptr; + + /* the samples in the ringbuffer have the channels interleaved, we need to + * deinterleave into the jack target buffers */ + for (i = 0; i < nframes; i++) { + for (j = 0; j < channels; j++) { + buffers[j][i] = *data++; + } + } + + /* clear written samples in the ringbuffer */ + gst_ring_buffer_clear (buf, readseg); + + /* we wrote one segment */ + gst_ring_buffer_advance (buf, 1); + } else { + GST_DEBUG_OBJECT (sink, "write %d frames silence", nframes); + /* We are not allowed to read from the ringbuffer, write silence to all + * jack output buffers */ + for (i = 0; i < channels; i++) { + memset (buffers[i], 0, nframes * sizeof (sample_t)); + } + } + return 0; + + /* ERRORS */ +wrong_size: + { + GST_ERROR_OBJECT (sink, "nbytes (%d) != flen (%d)", + (gint) (nframes * sizeof (sample_t)), flen); + return 1; + } +} + +/* we error out */ +static int +jack_sample_rate_cb (jack_nframes_t nframes, void *arg) +{ + GstJackAudioSink *sink; + GstJackRingBuffer *abuf; + + abuf = GST_JACK_RING_BUFFER_CAST (arg); + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (arg)); + + if (abuf->sample_rate != -1 && abuf->sample_rate != nframes) + goto not_supported; + + return 0; + + /* ERRORS */ +not_supported: + { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, + (NULL), ("Jack changed the sample rate, which is not supported")); + return 1; + } +} + +/* we error out */ +static int +jack_buffer_size_cb (jack_nframes_t nframes, void *arg) +{ + GstJackAudioSink *sink; + GstJackRingBuffer *abuf; + + abuf = GST_JACK_RING_BUFFER_CAST (arg); + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (arg)); + + if (abuf->buffer_size != -1 && abuf->buffer_size != nframes) + goto not_supported; + + return 0; + + /* ERRORS */ +not_supported: + { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, + (NULL), ("Jack changed the buffer size, which is not supported")); + return 1; + } +} + +static void +jack_shutdown_cb (void *arg) +{ + GstJackAudioSink *sink; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (arg)); + + GST_DEBUG_OBJECT (sink, "shutdown"); + + GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, + (NULL), ("Jack server shutdown")); +} + +static void +gst_jack_ring_buffer_init (GstJackRingBuffer * buf, + GstJackRingBufferClass * g_class) +{ + buf->channels = -1; + buf->buffer_size = -1; + buf->sample_rate = -1; +} + +/* the _open_device method should make a connection with the server + */ +static gboolean +gst_jack_ring_buffer_open_device (GstRingBuffer * buf) +{ + GstJackAudioSink *sink; + jack_status_t status = 0; + const gchar *name; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (sink, "open"); + + name = g_get_application_name (); + if (!name) + name = "GStreamer"; + + sink->client = gst_jack_audio_client_new (name, sink->server, + sink->jclient, + GST_JACK_CLIENT_SINK, + jack_shutdown_cb, + jack_process_cb, jack_buffer_size_cb, jack_sample_rate_cb, buf, &status); + if (sink->client == NULL) + goto could_not_open; + + GST_DEBUG_OBJECT (sink, "opened"); + + return TRUE; + + /* ERRORS */ +could_not_open: + { + if (status & JackServerFailed) { + GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, + (_("Jack server not found")), + ("Cannot connect to the Jack server (status %d)", status)); + } else { + GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, + (NULL), ("Jack client open error (status %d)", status)); + } + return FALSE; + } +} + +/* close the connection with the server + */ +static gboolean +gst_jack_ring_buffer_close_device (GstRingBuffer * buf) +{ + GstJackAudioSink *sink; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (sink, "close"); + + gst_jack_audio_sink_free_channels (sink); + gst_jack_audio_client_free (sink->client); + sink->client = NULL; + + return TRUE; +} + +/* allocate a buffer and setup resources to process the audio samples of + * the format as specified in @spec. + * + * We allocate N jack ports, one for each channel. If we are asked to + * automatically make a connection with physical ports, we connect as many + * ports as there are physical ports, leaving leftover ports unconnected. + * + * It is assumed that samplerate and number of channels are acceptable since our + * getcaps method will always provide correct values. If unacceptable caps are + * received for some reason, we fail here. + */ +static gboolean +gst_jack_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) +{ + GstJackAudioSink *sink; + GstJackRingBuffer *abuf; + const char **ports; + gint sample_rate, buffer_size; + gint i, channels, res; + jack_client_t *client; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + abuf = GST_JACK_RING_BUFFER_CAST (buf); + + GST_DEBUG_OBJECT (sink, "acquire"); + + client = gst_jack_audio_client_get_client (sink->client); + + /* sample rate must be that of the server */ + sample_rate = jack_get_sample_rate (client); + if (sample_rate != spec->rate) + goto wrong_samplerate; + + channels = spec->channels; + + if (!gst_jack_audio_sink_allocate_channels (sink, channels)) + goto out_of_ports; + + buffer_size = jack_get_buffer_size (client); + + /* the segment size in bytes, this is large enough to hold a buffer of 32bit floats + * for all channels */ + spec->segsize = buffer_size * sizeof (gfloat) * channels; + spec->latency_time = gst_util_uint64_scale (spec->segsize, + (GST_SECOND / GST_USECOND), spec->rate * spec->bytes_per_sample); + /* segtotal based on buffer-time latency */ + spec->segtotal = spec->buffer_time / spec->latency_time; + if (spec->segtotal < 2) { + spec->segtotal = 2; + spec->buffer_time = spec->latency_time * spec->segtotal; + } + + GST_DEBUG_OBJECT (sink, "buffer time: %" G_GINT64_FORMAT " usec", + spec->buffer_time); + GST_DEBUG_OBJECT (sink, "latency time: %" G_GINT64_FORMAT " usec", + spec->latency_time); + GST_DEBUG_OBJECT (sink, "buffer_size %d, segsize %d, segtotal %d", + buffer_size, spec->segsize, spec->segtotal); + + /* allocate the ringbuffer memory now */ + buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize); + memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data)); + + if ((res = gst_jack_audio_client_set_active (sink->client, TRUE))) + goto could_not_activate; + + /* if we need to automatically connect the ports, do so now. We must do this + * after activating the client. */ + if (sink->connect == GST_JACK_CONNECT_AUTO + || sink->connect == GST_JACK_CONNECT_AUTO_FORCED) { + /* find all the physical input ports. A physical input port is a port + * associated with a hardware device. Someone needs connect to a physical + * port in order to hear something. */ + ports = jack_get_ports (client, NULL, NULL, + JackPortIsPhysical | JackPortIsInput); + if (ports == NULL) { + /* no ports? fine then we don't do anything except for posting a warning + * message. */ + GST_ELEMENT_WARNING (sink, RESOURCE, NOT_FOUND, (NULL), + ("No physical input ports found, leaving ports unconnected")); + goto done; + } + + for (i = 0; i < channels; i++) { + /* stop when all input ports are exhausted */ + if (ports[i] == NULL) { + /* post a warning that we could not connect all ports */ + GST_ELEMENT_WARNING (sink, RESOURCE, NOT_FOUND, (NULL), + ("No more physical ports, leaving some ports unconnected")); + break; + } + GST_DEBUG_OBJECT (sink, "try connecting to %s", + jack_port_name (sink->ports[i])); + /* connect the port to a physical port */ + res = jack_connect (client, jack_port_name (sink->ports[i]), ports[i]); + if (res != 0 && res != EEXIST) + goto cannot_connect; + } + free (ports); + } +done: + + abuf->sample_rate = sample_rate; + abuf->buffer_size = buffer_size; + abuf->channels = spec->channels; + + return TRUE; + + /* ERRORS */ +wrong_samplerate: + { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), + ("Wrong samplerate, server is running at %d and we received %d", + sample_rate, spec->rate)); + return FALSE; + } +out_of_ports: + { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), + ("Cannot allocate more Jack ports")); + return FALSE; + } +could_not_activate: + { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), + ("Could not activate client (%d:%s)", res, g_strerror (res))); + return FALSE; + } +cannot_connect: + { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), + ("Could not connect output ports to physical ports (%d:%s)", + res, g_strerror (res))); + free (ports); + return FALSE; + } +} + +/* function is called with LOCK */ +static gboolean +gst_jack_ring_buffer_release (GstRingBuffer * buf) +{ + GstJackAudioSink *sink; + GstJackRingBuffer *abuf; + gint res; + + abuf = GST_JACK_RING_BUFFER_CAST (buf); + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (sink, "release"); + + if ((res = gst_jack_audio_client_set_active (sink->client, FALSE))) { + /* we only warn, this means the server is probably shut down and the client + * is gone anyway. */ + GST_ELEMENT_WARNING (sink, RESOURCE, CLOSE, (NULL), + ("Could not deactivate Jack client (%d)", res)); + } + + abuf->channels = -1; + abuf->buffer_size = -1; + abuf->sample_rate = -1; + + /* free the buffer */ + gst_buffer_unref (buf->data); + buf->data = NULL; + + return TRUE; +} + +static gboolean +gst_jack_ring_buffer_start (GstRingBuffer * buf) +{ + GstJackAudioSink *sink; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (sink, "start"); + + return TRUE; +} + +static gboolean +gst_jack_ring_buffer_pause (GstRingBuffer * buf) +{ + GstJackAudioSink *sink; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (sink, "pause"); + + return TRUE; +} + +static gboolean +gst_jack_ring_buffer_stop (GstRingBuffer * buf) +{ + GstJackAudioSink *sink; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (sink, "stop"); + + return TRUE; +} + +static guint +gst_jack_ring_buffer_delay (GstRingBuffer * buf) +{ + GstJackAudioSink *sink; + guint i, res = 0, latency; + jack_client_t *client; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + client = gst_jack_audio_client_get_client (sink->client); + + for (i = 0; i < sink->port_count; i++) { + latency = jack_port_get_total_latency (client, sink->ports[i]); + if (latency > res) + res = latency; + } + + GST_LOG_OBJECT (sink, "delay %u", res); + + return res; +} + +static GstStaticPadTemplate jackaudiosink_sink_factory = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-float, " + "endianness = (int) BYTE_ORDER, " + "width = (int) 32, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]") + ); + +/* AudioSink signals and args */ +enum +{ + /* FILL ME */ + SIGNAL_LAST +}; + +#define DEFAULT_PROP_CONNECT GST_JACK_CONNECT_AUTO +#define DEFAULT_PROP_SERVER NULL + +enum +{ + PROP_0, + PROP_CONNECT, + PROP_SERVER, + PROP_CLIENT, + PROP_LAST +}; + +#define _do_init(bla) \ + GST_DEBUG_CATEGORY_INIT (gst_jack_audio_sink_debug, "jacksink", 0, "jacksink element"); + +GST_BOILERPLATE_FULL (GstJackAudioSink, gst_jack_audio_sink, GstBaseAudioSink, + GST_TYPE_BASE_AUDIO_SINK, _do_init); + +static void gst_jack_audio_sink_dispose (GObject * object); +static void gst_jack_audio_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_jack_audio_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static GstCaps *gst_jack_audio_sink_getcaps (GstBaseSink * bsink); +static GstRingBuffer *gst_jack_audio_sink_create_ringbuffer (GstBaseAudioSink * + sink); + +static void +gst_jack_audio_sink_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details_simple (element_class, "Audio Sink (Jack)", + "Sink/Audio", "Output audio to a JACK server", + "Wim Taymans "); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&jackaudiosink_sink_factory)); +} + +static void +gst_jack_audio_sink_class_init (GstJackAudioSinkClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + GstBaseAudioSinkClass *gstbaseaudiosink_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; + gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass; + + gobject_class->dispose = gst_jack_audio_sink_dispose; + gobject_class->get_property = gst_jack_audio_sink_get_property; + gobject_class->set_property = gst_jack_audio_sink_set_property; + + g_object_class_install_property (gobject_class, PROP_CONNECT, + g_param_spec_enum ("connect", "Connect", + "Specify how the output ports will be connected", + GST_TYPE_JACK_CONNECT, DEFAULT_PROP_CONNECT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_SERVER, + g_param_spec_string ("server", "Server", + "The Jack server to connect to (NULL = default)", + DEFAULT_PROP_SERVER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_CLIENT, + g_param_spec_boxed ("client", "JackClient", "Handle for jack client", + GST_TYPE_JACK_CLIENT, + GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_jack_audio_sink_getcaps); + + gstbaseaudiosink_class->create_ringbuffer = + GST_DEBUG_FUNCPTR (gst_jack_audio_sink_create_ringbuffer); + + /* ref class from a thread-safe context to work around missing bit of + * thread-safety in GObject */ + g_type_class_ref (GST_TYPE_JACK_RING_BUFFER); + + gst_jack_audio_client_init (); +} + +static void +gst_jack_audio_sink_init (GstJackAudioSink * sink, + GstJackAudioSinkClass * g_class) +{ + sink->connect = DEFAULT_PROP_CONNECT; + sink->server = g_strdup (DEFAULT_PROP_SERVER); + sink->jclient = NULL; + sink->ports = NULL; + sink->port_count = 0; +} + +static void +gst_jack_audio_sink_dispose (GObject * object) +{ + GstJackAudioSink *sink = GST_JACK_AUDIO_SINK (object); + + gst_caps_replace (&sink->caps, NULL); + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_jack_audio_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstJackAudioSink *sink; + + sink = GST_JACK_AUDIO_SINK (object); + + switch (prop_id) { + case PROP_CONNECT: + sink->connect = g_value_get_enum (value); + break; + case PROP_SERVER: + g_free (sink->server); + sink->server = g_value_dup_string (value); + break; + case PROP_CLIENT: + if (GST_STATE (sink) == GST_STATE_NULL || + GST_STATE (sink) == GST_STATE_READY) { + sink->jclient = g_value_get_boxed (value); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_jack_audio_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstJackAudioSink *sink; + + sink = GST_JACK_AUDIO_SINK (object); + + switch (prop_id) { + case PROP_CONNECT: + g_value_set_enum (value, sink->connect); + break; + case PROP_SERVER: + g_value_set_string (value, sink->server); + break; + case PROP_CLIENT: + g_value_set_boxed (value, sink->jclient); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstCaps * +gst_jack_audio_sink_getcaps (GstBaseSink * bsink) +{ + GstJackAudioSink *sink = GST_JACK_AUDIO_SINK (bsink); + const char **ports; + gint min, max; + gint rate; + jack_client_t *client; + + if (sink->client == NULL) + goto no_client; + + client = gst_jack_audio_client_get_client (sink->client); + + if (sink->connect == GST_JACK_CONNECT_AUTO) { + /* get a port count, this is the number of channels we can automatically + * connect. */ + ports = jack_get_ports (client, NULL, NULL, + JackPortIsPhysical | JackPortIsInput); + max = 0; + if (ports != NULL) { + for (; ports[max]; max++); + free (ports); + } else + max = 0; + } else { + /* we allow any number of pads, something else is going to connect the + * pads. */ + max = G_MAXINT; + } + min = MIN (1, max); + + rate = jack_get_sample_rate (client); + + GST_DEBUG_OBJECT (sink, "got %d-%d ports, samplerate: %d", min, max, rate); + + if (!sink->caps) { + sink->caps = gst_caps_new_simple ("audio/x-raw-float", + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "width", G_TYPE_INT, 32, + "rate", G_TYPE_INT, rate, + "channels", GST_TYPE_INT_RANGE, min, max, NULL); + } + GST_INFO_OBJECT (sink, "returning caps %" GST_PTR_FORMAT, sink->caps); + + return gst_caps_ref (sink->caps); + + /* ERRORS */ +no_client: + { + GST_DEBUG_OBJECT (sink, "device not open, using template caps"); + /* base class will get template caps for us when we return NULL */ + return NULL; + } +} + +static GstRingBuffer * +gst_jack_audio_sink_create_ringbuffer (GstBaseAudioSink * sink) +{ + GstRingBuffer *buffer; + + buffer = g_object_new (GST_TYPE_JACK_RING_BUFFER, NULL); + GST_DEBUG_OBJECT (sink, "created ringbuffer @%p", buffer); + + return buffer; +} diff --git a/ext/jack/gstjackaudiosink.h b/ext/jack/gstjackaudiosink.h new file mode 100644 index 0000000000..def423329d --- /dev/null +++ b/ext/jack/gstjackaudiosink.h @@ -0,0 +1,78 @@ +/* GStreamer + * Copyright (C) 2006 Wim Taymans + * + * gstjacksink.h: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_JACK_AUDIO_SINK_H__ +#define __GST_JACK_AUDIO_SINK_H__ + +#include + +#include +#include + +#include "gstjack.h" +#include "gstjackaudioclient.h" + +G_BEGIN_DECLS + +#define GST_TYPE_JACK_AUDIO_SINK (gst_jack_audio_sink_get_type()) +#define GST_JACK_AUDIO_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_JACK_AUDIO_SINK,GstJackAudioSink)) +#define GST_JACK_AUDIO_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_JACK_AUDIO_SINK,GstJackAudioSinkClass)) +#define GST_JACK_AUDIO_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_JACK_AUDIO_SINK,GstJackAudioSinkClass)) +#define GST_IS_JACK_AUDIO_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_JACK_AUDIO_SINK)) +#define GST_IS_JACK_AUDIO_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_JACK_AUDIO_SINK)) + +typedef struct _GstJackAudioSink GstJackAudioSink; +typedef struct _GstJackAudioSinkClass GstJackAudioSinkClass; + +/** + * GstJackAudioSink: + * + * Opaque #GstJackAudioSink. + */ +struct _GstJackAudioSink { + GstBaseAudioSink element; + + /*< private >*/ + /* cached caps */ + GstCaps *caps; + + /* properties */ + GstJackConnect connect; + gchar *server; + jack_client_t *jclient; + + /* our client */ + GstJackAudioClient *client; + + /* our ports */ + jack_port_t **ports; + int port_count; +}; + +struct _GstJackAudioSinkClass { + GstBaseAudioSinkClass parent_class; +}; + +GType gst_jack_audio_sink_get_type (void); + +G_END_DECLS + +#endif /* __GST_JACK_AUDIO_SINK_H__ */ diff --git a/ext/jack/gstjackaudiosrc.c b/ext/jack/gstjackaudiosrc.c new file mode 100644 index 0000000000..08b325ead7 --- /dev/null +++ b/ext/jack/gstjackaudiosrc.c @@ -0,0 +1,874 @@ +/* GStreamer + * Copyright (C) 2008 Tristan Matthews + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-jackaudiosrc + * @see_also: #GstBaseAudioSrc, #GstRingBuffer + * + * A Src that inputs data from Jack ports. + * + * It will create N Jack ports named in_<name>_<num> where + * <name> is the element name and <num> is starting from 1. + * Each port corresponds to a gstreamer channel. + * + * The samplerate as exposed on the caps is always the same as the samplerate of + * the jack server. + * + * When the #GstJackAudioSrc:connect property is set to auto, this element + * will try to connect each input port to a random physical jack output pin. + * + * When the #GstJackAudioSrc:connect property is set to none, the element will + * accept any number of output channels and will create (but not connect) an + * input port for each channel. + * + * The element will generate an error when the Jack server is shut down when it + * was PAUSED or PLAYING. This element does not support dynamic rate and buffer + * size changes at runtime. + * + * + * Example launch line + * |[ + * gst-launch jackaudiosrc connect=0 ! jackaudiosink connect=0 + * ]| Get audio input into gstreamer from jack. + * + * + * Last reviewed on 2008-07-22 (0.10.4) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "gstjackaudiosrc.h" +#include "gstjackringbuffer.h" +#include "gstjackutil.h" + +GST_DEBUG_CATEGORY_STATIC (gst_jack_audio_src_debug); +#define GST_CAT_DEFAULT gst_jack_audio_src_debug + +static gboolean +gst_jack_audio_src_allocate_channels (GstJackAudioSrc * src, gint channels) +{ + jack_client_t *client; + + client = gst_jack_audio_client_get_client (src->client); + + /* remove ports we don't need */ + while (src->port_count > channels) + jack_port_unregister (client, src->ports[--src->port_count]); + + /* alloc enough input ports */ + src->ports = g_realloc (src->ports, sizeof (jack_port_t *) * channels); + src->buffers = g_realloc (src->buffers, sizeof (sample_t *) * channels); + + /* create an input port for each channel */ + while (src->port_count < channels) { + gchar *name; + + /* port names start from 1 and are local to the element */ + name = + g_strdup_printf ("in_%s_%d", GST_ELEMENT_NAME (src), + src->port_count + 1); + src->ports[src->port_count] = + jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, 0); + if (src->ports[src->port_count] == NULL) + return FALSE; + + src->port_count++; + + g_free (name); + } + return TRUE; +} + +static void +gst_jack_audio_src_free_channels (GstJackAudioSrc * src) +{ + gint res, i = 0; + jack_client_t *client; + + client = gst_jack_audio_client_get_client (src->client); + + /* get rid of all ports */ + while (src->port_count) { + GST_LOG_OBJECT (src, "unregister port %d", i); + if ((res = jack_port_unregister (client, src->ports[i++]))) + GST_DEBUG_OBJECT (src, "unregister of port failed (%d)", res); + + src->port_count--; + } + g_free (src->ports); + src->ports = NULL; + g_free (src->buffers); + src->buffers = NULL; +} + +/* ringbuffer abstract base class */ +static GType +gst_jack_ring_buffer_get_type (void) +{ + static volatile gsize ringbuffer_type = 0; + + if (g_once_init_enter (&ringbuffer_type)) { + static const GTypeInfo ringbuffer_info = { sizeof (GstJackRingBufferClass), + NULL, + NULL, + (GClassInitFunc) gst_jack_ring_buffer_class_init, + NULL, + NULL, + sizeof (GstJackRingBuffer), + 0, + (GInstanceInitFunc) gst_jack_ring_buffer_init, + NULL + }; + GType tmp = g_type_register_static (GST_TYPE_RING_BUFFER, + "GstJackAudioSrcRingBuffer", &ringbuffer_info, 0); + g_once_init_leave (&ringbuffer_type, tmp); + } + + return (GType) ringbuffer_type; +} + +static void +gst_jack_ring_buffer_class_init (GstJackRingBufferClass * klass) +{ + GObjectClass *gobject_class; + GstObjectClass *gstobject_class; + GstRingBufferClass *gstringbuffer_class; + + gobject_class = (GObjectClass *) klass; + gstobject_class = (GstObjectClass *) klass; + gstringbuffer_class = (GstRingBufferClass *) klass; + + ring_parent_class = g_type_class_peek_parent (klass); + + gstringbuffer_class->open_device = + GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_open_device); + gstringbuffer_class->close_device = + GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_close_device); + gstringbuffer_class->acquire = + GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_acquire); + gstringbuffer_class->release = + GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_release); + gstringbuffer_class->start = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_start); + gstringbuffer_class->pause = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_pause); + gstringbuffer_class->resume = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_start); + gstringbuffer_class->stop = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_stop); + + gstringbuffer_class->delay = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_delay); +} + +/* this is the callback of jack. This should be RT-safe. + * Writes samples from the jack input port's buffer to the gst ring buffer. + */ +static int +jack_process_cb (jack_nframes_t nframes, void *arg) +{ + GstJackAudioSrc *src; + GstRingBuffer *buf; + gint len; + guint8 *writeptr; + gint writeseg; + gint channels, i, j, flen; + sample_t *data; + + buf = GST_RING_BUFFER_CAST (arg); + src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf)); + + channels = buf->spec.channels; + + /* get input buffers */ + for (i = 0; i < channels; i++) + src->buffers[i] = + (sample_t *) jack_port_get_buffer (src->ports[i], nframes); + + if (gst_ring_buffer_prepare_read (buf, &writeseg, &writeptr, &len)) { + flen = len / channels; + + /* the number of samples must be exactly the segment size */ + if (nframes * sizeof (sample_t) != flen) + goto wrong_size; + + /* the samples in the jack input buffers have to be interleaved into the + * ringbuffer */ + data = (sample_t *) writeptr; + for (i = 0; i < nframes; ++i) + for (j = 0; j < channels; ++j) + *data++ = src->buffers[j][i]; + + GST_DEBUG ("copy %d frames: %p, %d bytes, %d channels", nframes, writeptr, + len / channels, channels); + + /* we wrote one segment */ + gst_ring_buffer_advance (buf, 1); + } + return 0; + + /* ERRORS */ +wrong_size: + { + GST_ERROR_OBJECT (src, "nbytes (%d) != flen (%d)", + (gint) (nframes * sizeof (sample_t)), flen); + return 1; + } +} + +/* we error out */ +static int +jack_sample_rate_cb (jack_nframes_t nframes, void *arg) +{ + GstJackAudioSrc *src; + GstJackRingBuffer *abuf; + + abuf = GST_JACK_RING_BUFFER_CAST (arg); + src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (arg)); + + if (abuf->sample_rate != -1 && abuf->sample_rate != nframes) + goto not_supported; + + return 0; + + /* ERRORS */ +not_supported: + { + GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, + (NULL), ("Jack changed the sample rate, which is not supported")); + return 1; + } +} + +/* we error out */ +static int +jack_buffer_size_cb (jack_nframes_t nframes, void *arg) +{ + GstJackAudioSrc *src; + GstJackRingBuffer *abuf; + + abuf = GST_JACK_RING_BUFFER_CAST (arg); + src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (arg)); + + if (abuf->buffer_size != -1 && abuf->buffer_size != nframes) + goto not_supported; + + return 0; + + /* ERRORS */ +not_supported: + { + GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, + (NULL), ("Jack changed the buffer size, which is not supported")); + return 1; + } +} + +static void +jack_shutdown_cb (void *arg) +{ + GstJackAudioSrc *src; + + src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (arg)); + + GST_DEBUG_OBJECT (src, "shutdown"); + + GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, + (NULL), ("Jack server shutdown")); +} + +static void +gst_jack_ring_buffer_init (GstJackRingBuffer * buf, + GstJackRingBufferClass * g_class) +{ + buf->channels = -1; + buf->buffer_size = -1; + buf->sample_rate = -1; +} + +/* the _open_device method should make a connection with the server +*/ +static gboolean +gst_jack_ring_buffer_open_device (GstRingBuffer * buf) +{ + GstJackAudioSrc *src; + jack_status_t status = 0; + const gchar *name; + + src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (src, "open"); + + name = g_get_application_name (); + if (!name) + name = "GStreamer"; + + src->client = gst_jack_audio_client_new (name, src->server, + src->jclient, + GST_JACK_CLIENT_SOURCE, + jack_shutdown_cb, + jack_process_cb, jack_buffer_size_cb, jack_sample_rate_cb, buf, &status); + if (src->client == NULL) + goto could_not_open; + + GST_DEBUG_OBJECT (src, "opened"); + + return TRUE; + + /* ERRORS */ +could_not_open: + { + if (status & JackServerFailed) { + GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, + (_("Jack server not found")), + ("Cannot connect to the Jack server (status %d)", status)); + } else { + GST_ELEMENT_ERROR (src, RESOURCE, OPEN_WRITE, + (NULL), ("Jack client open error (status %d)", status)); + } + return FALSE; + } +} + +/* close the connection with the server +*/ +static gboolean +gst_jack_ring_buffer_close_device (GstRingBuffer * buf) +{ + GstJackAudioSrc *src; + + src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (src, "close"); + + gst_jack_audio_src_free_channels (src); + gst_jack_audio_client_free (src->client); + src->client = NULL; + + return TRUE; +} + + +/* allocate a buffer and setup resources to process the audio samples of + * the format as specified in @spec. + * + * We allocate N jack ports, one for each channel. If we are asked to + * automatically make a connection with physical ports, we connect as many + * ports as there are physical ports, leaving leftover ports unconnected. + * + * It is assumed that samplerate and number of channels are acceptable since our + * getcaps method will always provide correct values. If unacceptable caps are + * received for some reason, we fail here. + */ +static gboolean +gst_jack_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) +{ + GstJackAudioSrc *src; + GstJackRingBuffer *abuf; + const char **ports; + gint sample_rate, buffer_size; + gint i, channels, res; + jack_client_t *client; + + src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf)); + abuf = GST_JACK_RING_BUFFER_CAST (buf); + + GST_DEBUG_OBJECT (src, "acquire"); + + client = gst_jack_audio_client_get_client (src->client); + + /* sample rate must be that of the server */ + sample_rate = jack_get_sample_rate (client); + if (sample_rate != spec->rate) + goto wrong_samplerate; + + channels = spec->channels; + + if (!gst_jack_audio_src_allocate_channels (src, channels)) + goto out_of_ports; + + gst_jack_set_layout_on_caps (&spec->caps, channels); + + buffer_size = jack_get_buffer_size (client); + + /* the segment size in bytes, this is large enough to hold a buffer of 32bit floats + * for all channels */ + spec->segsize = buffer_size * sizeof (gfloat) * channels; + spec->latency_time = gst_util_uint64_scale (spec->segsize, + (GST_SECOND / GST_USECOND), spec->rate * spec->bytes_per_sample); + /* segtotal based on buffer-time latency */ + spec->segtotal = spec->buffer_time / spec->latency_time; + if (spec->segtotal < 2) { + spec->segtotal = 2; + spec->buffer_time = spec->latency_time * spec->segtotal; + } + + GST_DEBUG_OBJECT (src, "buffer time: %" G_GINT64_FORMAT " usec", + spec->buffer_time); + GST_DEBUG_OBJECT (src, "latency time: %" G_GINT64_FORMAT " usec", + spec->latency_time); + GST_DEBUG_OBJECT (src, "buffer_size %d, segsize %d, segtotal %d", + buffer_size, spec->segsize, spec->segtotal); + + /* allocate the ringbuffer memory now */ + buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize); + memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data)); + + if ((res = gst_jack_audio_client_set_active (src->client, TRUE))) + goto could_not_activate; + + /* if we need to automatically connect the ports, do so now. We must do this + * after activating the client. */ + if (src->connect == GST_JACK_CONNECT_AUTO + || src->connect == GST_JACK_CONNECT_AUTO_FORCED) { + /* find all the physical output ports. A physical output port is a port + * associated with a hardware device. Someone needs connect to a physical + * port in order to capture something. */ + ports = + jack_get_ports (client, NULL, NULL, + JackPortIsPhysical | JackPortIsOutput); + if (ports == NULL) { + /* no ports? fine then we don't do anything except for posting a warning + * message. */ + GST_ELEMENT_WARNING (src, RESOURCE, NOT_FOUND, (NULL), + ("No physical output ports found, leaving ports unconnected")); + goto done; + } + + for (i = 0; i < channels; i++) { + /* stop when all output ports are exhausted */ + if (ports[i] == NULL) { + /* post a warning that we could not connect all ports */ + GST_ELEMENT_WARNING (src, RESOURCE, NOT_FOUND, (NULL), + ("No more physical ports, leaving some ports unconnected")); + break; + } + GST_DEBUG_OBJECT (src, "try connecting to %s", + jack_port_name (src->ports[i])); + + /* connect the physical port to a port */ + res = jack_connect (client, ports[i], jack_port_name (src->ports[i])); + if (res != 0 && res != EEXIST) + goto cannot_connect; + } + free (ports); + } +done: + + abuf->sample_rate = sample_rate; + abuf->buffer_size = buffer_size; + abuf->channels = spec->channels; + + return TRUE; + + /* ERRORS */ +wrong_samplerate: + { + GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL), + ("Wrong samplerate, server is running at %d and we received %d", + sample_rate, spec->rate)); + return FALSE; + } +out_of_ports: + { + GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL), + ("Cannot allocate more Jack ports")); + return FALSE; + } +could_not_activate: + { + GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL), + ("Could not activate client (%d:%s)", res, g_strerror (res))); + return FALSE; + } +cannot_connect: + { + GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL), + ("Could not connect input ports to physical ports (%d:%s)", + res, g_strerror (res))); + free (ports); + return FALSE; + } +} + +/* function is called with LOCK */ +static gboolean +gst_jack_ring_buffer_release (GstRingBuffer * buf) +{ + GstJackAudioSrc *src; + GstJackRingBuffer *abuf; + gint res; + + abuf = GST_JACK_RING_BUFFER_CAST (buf); + src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (src, "release"); + + if ((res = gst_jack_audio_client_set_active (src->client, FALSE))) { + /* we only warn, this means the server is probably shut down and the client + * is gone anyway. */ + GST_ELEMENT_WARNING (src, RESOURCE, CLOSE, (NULL), + ("Could not deactivate Jack client (%d)", res)); + } + + abuf->channels = -1; + abuf->buffer_size = -1; + abuf->sample_rate = -1; + + /* free the buffer */ + gst_buffer_unref (buf->data); + buf->data = NULL; + + return TRUE; +} + +static gboolean +gst_jack_ring_buffer_start (GstRingBuffer * buf) +{ + GstJackAudioSrc *src; + + src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (src, "start"); + + return TRUE; +} + +static gboolean +gst_jack_ring_buffer_pause (GstRingBuffer * buf) +{ + GstJackAudioSrc *src; + + src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (src, "pause"); + + return TRUE; +} + +static gboolean +gst_jack_ring_buffer_stop (GstRingBuffer * buf) +{ + GstJackAudioSrc *src; + + src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (src, "stop"); + + return TRUE; +} + +static guint +gst_jack_ring_buffer_delay (GstRingBuffer * buf) +{ + GstJackAudioSrc *src; + guint i, res = 0, latency; + jack_client_t *client; + + src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf)); + client = gst_jack_audio_client_get_client (src->client); + + for (i = 0; i < src->port_count; i++) { + latency = jack_port_get_total_latency (client, src->ports[i]); + if (latency > res) + res = latency; + } + + GST_DEBUG_OBJECT (src, "delay %u", res); + + return res; +} + +/* Audiosrc signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +#define DEFAULT_PROP_CONNECT GST_JACK_CONNECT_AUTO +#define DEFAULT_PROP_SERVER NULL + +enum +{ + PROP_0, + PROP_CONNECT, + PROP_SERVER, + PROP_CLIENT, + PROP_LAST +}; + + +/* the capabilities of the inputs and outputs. + * + * describe the real formats here. + */ + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-float, " + "endianness = (int) BYTE_ORDER, " + "width = (int) 32, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]") + ); + +#define _do_init(bla) \ + GST_DEBUG_CATEGORY_INIT(gst_jack_audio_src_debug, "jacksrc", 0, "jacksrc element"); + +GST_BOILERPLATE_FULL (GstJackAudioSrc, gst_jack_audio_src, GstBaseAudioSrc, + GST_TYPE_BASE_AUDIO_SRC, _do_init); + +static void gst_jack_audio_src_dispose (GObject * object); +static void gst_jack_audio_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_jack_audio_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static GstCaps *gst_jack_audio_src_getcaps (GstBaseSrc * bsrc); +static GstRingBuffer *gst_jack_audio_src_create_ringbuffer (GstBaseAudioSrc * + src); + +/* GObject vmethod implementations */ + +static void +gst_jack_audio_src_base_init (gpointer gclass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_factory)); + gst_element_class_set_details_simple (element_class, "Audio Source (Jack)", + "Source/Audio", "Captures audio from a JACK server", + "Tristan Matthews "); +} + +/* initialize the jack_audio_src's class */ +static void +gst_jack_audio_src_class_init (GstJackAudioSrcClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSrcClass *gstbasesrc_class; + GstBaseAudioSrcClass *gstbaseaudiosrc_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gstbasesrc_class = (GstBaseSrcClass *) klass; + gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass; + + gobject_class->dispose = gst_jack_audio_src_dispose; + gobject_class->set_property = gst_jack_audio_src_set_property; + gobject_class->get_property = gst_jack_audio_src_get_property; + + g_object_class_install_property (gobject_class, PROP_CONNECT, + g_param_spec_enum ("connect", "Connect", + "Specify how the input ports will be connected", + GST_TYPE_JACK_CONNECT, DEFAULT_PROP_CONNECT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_SERVER, + g_param_spec_string ("server", "Server", + "The Jack server to connect to (NULL = default)", + DEFAULT_PROP_SERVER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_CLIENT, + g_param_spec_boxed ("client", "JackClient", "Handle for jack client", + GST_TYPE_JACK_CLIENT, + GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_jack_audio_src_getcaps); + gstbaseaudiosrc_class->create_ringbuffer = + GST_DEBUG_FUNCPTR (gst_jack_audio_src_create_ringbuffer); + + /* ref class from a thread-safe context to work around missing bit of + * thread-safety in GObject */ + g_type_class_ref (GST_TYPE_JACK_RING_BUFFER); + + gst_jack_audio_client_init (); +} + +/* initialize the new element + * instantiate pads and add them to element + * set pad calback functions + * initialize instance structure + */ +static void +gst_jack_audio_src_init (GstJackAudioSrc * src, GstJackAudioSrcClass * gclass) +{ + //gst_base_src_set_live(GST_BASE_SRC (src), TRUE); + src->connect = DEFAULT_PROP_CONNECT; + src->server = g_strdup (DEFAULT_PROP_SERVER); + src->jclient = NULL; + src->ports = NULL; + src->port_count = 0; + src->buffers = NULL; +} + +static void +gst_jack_audio_src_dispose (GObject * object) +{ + GstJackAudioSrc *src = GST_JACK_AUDIO_SRC (object); + + gst_caps_replace (&src->caps, NULL); + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_jack_audio_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstJackAudioSrc *src = GST_JACK_AUDIO_SRC (object); + + switch (prop_id) { + case PROP_CONNECT: + src->connect = g_value_get_enum (value); + break; + case PROP_SERVER: + g_free (src->server); + src->server = g_value_dup_string (value); + break; + case PROP_CLIENT: + if (GST_STATE (src) == GST_STATE_NULL || + GST_STATE (src) == GST_STATE_READY) { + src->jclient = g_value_get_boxed (value); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_jack_audio_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstJackAudioSrc *src = GST_JACK_AUDIO_SRC (object); + + switch (prop_id) { + case PROP_CONNECT: + g_value_set_enum (value, src->connect); + break; + case PROP_SERVER: + g_value_set_string (value, src->server); + break; + case PROP_CLIENT: + g_value_set_boxed (value, src->jclient); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstCaps * +gst_jack_audio_src_getcaps (GstBaseSrc * bsrc) +{ + GstJackAudioSrc *src = GST_JACK_AUDIO_SRC (bsrc); + const char **ports; + gint min, max; + gint rate; + jack_client_t *client; + + if (src->client == NULL) + goto no_client; + + client = gst_jack_audio_client_get_client (src->client); + + if (src->connect == GST_JACK_CONNECT_AUTO) { + /* get a port count, this is the number of channels we can automatically + * connect. */ + ports = jack_get_ports (client, NULL, NULL, + JackPortIsPhysical | JackPortIsOutput); + max = 0; + if (ports != NULL) { + for (; ports[max]; max++); + + free (ports); + } else + max = 0; + } else { + /* we allow any number of pads, something else is going to connect the + * pads. */ + max = G_MAXINT; + } + min = MIN (1, max); + + rate = jack_get_sample_rate (client); + + GST_DEBUG_OBJECT (src, "got %d-%d ports, samplerate: %d", min, max, rate); + + if (!src->caps) { + src->caps = gst_caps_new_simple ("audio/x-raw-float", + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "width", G_TYPE_INT, 32, + "rate", G_TYPE_INT, rate, + "channels", GST_TYPE_INT_RANGE, min, max, NULL); + } + GST_INFO_OBJECT (src, "returning caps %" GST_PTR_FORMAT, src->caps); + + return gst_caps_ref (src->caps); + + /* ERRORS */ +no_client: + { + GST_DEBUG_OBJECT (src, "device not open, using template caps"); + /* base class will get template caps for us when we return NULL */ + return NULL; + } +} + +static GstRingBuffer * +gst_jack_audio_src_create_ringbuffer (GstBaseAudioSrc * src) +{ + GstRingBuffer *buffer; + + buffer = g_object_new (GST_TYPE_JACK_RING_BUFFER, NULL); + GST_DEBUG_OBJECT (src, "created ringbuffer @%p", buffer); + + return buffer; +} diff --git a/ext/jack/gstjackaudiosrc.h b/ext/jack/gstjackaudiosrc.h new file mode 100644 index 0000000000..7e99b69df9 --- /dev/null +++ b/ext/jack/gstjackaudiosrc.h @@ -0,0 +1,97 @@ +/* GStreamer + * Copyright (C) 2008 Tristan Matthews + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_JACK_AUDIO_SRC_H__ +#define __GST_JACK_AUDIO_SRC_H__ + +#include + +#include +#include + +#include "gstjackaudioclient.h" +#include "gstjack.h" + +G_BEGIN_DECLS + +#define GST_TYPE_JACK_AUDIO_SRC (gst_jack_audio_src_get_type()) +#define GST_JACK_AUDIO_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_JACK_AUDIO_SRC,GstJackAudioSrc)) +#define GST_JACK_AUDIO_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_JACK_AUDIO_SRC,GstJackAudioSrcClass)) +#define GST_JACK_AUDIO_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_JACK_AUDIO_SRC,GstJackAudioSrcClass)) +#define GST_IS_JACK_AUDIO_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_JACK_AUDIO_SRC)) +#define GST_IS_JACK_AUDIO_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_JACK_AUDIO_SRC)) + +typedef struct _GstJackAudioSrc GstJackAudioSrc; +typedef struct _GstJackAudioSrcClass GstJackAudioSrcClass; + +struct _GstJackAudioSrc +{ + GstBaseAudioSrc src; + + /*< private >*/ + /* cached caps */ + GstCaps *caps; + + /* properties */ + GstJackConnect connect; + gchar *server; + jack_client_t *jclient; + + /* our client */ + GstJackAudioClient *client; + + /* our ports */ + jack_port_t **ports; + int port_count; + sample_t **buffers; +}; + +struct _GstJackAudioSrcClass +{ + GstBaseAudioSrcClass parent_class; +}; + +GType gst_jack_audio_src_get_type (void); + +G_END_DECLS + +#endif /* __GST_JACK_AUDIO_SRC_H__ */ diff --git a/ext/jack/gstjackringbuffer.h b/ext/jack/gstjackringbuffer.h new file mode 100644 index 0000000000..266fdfa31d --- /dev/null +++ b/ext/jack/gstjackringbuffer.h @@ -0,0 +1,88 @@ +/* + * GStreamer + * Copyright (C) 2006 Wim Taymans + * Copyright (C) 2008 Tristan Matthews + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_JACK_RING_BUFFER_H__ +#define __GST_JACK_RING_BUFFER_H__ + +#define GST_TYPE_JACK_RING_BUFFER (gst_jack_ring_buffer_get_type()) +#define GST_JACK_RING_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_JACK_RING_BUFFER,GstJackRingBuffer)) +#define GST_JACK_RING_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_JACK_RING_BUFFER,GstJackRingBufferClass)) +#define GST_JACK_RING_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_JACK_RING_BUFFER,GstJackRingBufferClass)) +#define GST_JACK_RING_BUFFER_CAST(obj) ((GstJackRingBuffer *)obj) +#define GST_IS_JACK_RING_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_JACK_RING_BUFFER)) +#define GST_IS_JACK_RING_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_JACK_RING_BUFFER)) + +typedef struct _GstJackRingBuffer GstJackRingBuffer; +typedef struct _GstJackRingBufferClass GstJackRingBufferClass; + +struct _GstJackRingBuffer +{ + GstRingBuffer object; + + gint sample_rate; + gint buffer_size; + gint channels; +}; + +struct _GstJackRingBufferClass +{ + GstRingBufferClass parent_class; +}; + +static void gst_jack_ring_buffer_class_init(GstJackRingBufferClass * klass); +static void gst_jack_ring_buffer_init(GstJackRingBuffer * ringbuffer, + GstJackRingBufferClass * klass); + +static GstRingBufferClass *ring_parent_class = NULL; + +static gboolean gst_jack_ring_buffer_open_device(GstRingBuffer * buf); +static gboolean gst_jack_ring_buffer_close_device(GstRingBuffer * buf); +static gboolean gst_jack_ring_buffer_acquire(GstRingBuffer * buf,GstRingBufferSpec * spec); +static gboolean gst_jack_ring_buffer_release(GstRingBuffer * buf); +static gboolean gst_jack_ring_buffer_start(GstRingBuffer * buf); +static gboolean gst_jack_ring_buffer_pause(GstRingBuffer * buf); +static gboolean gst_jack_ring_buffer_stop(GstRingBuffer * buf); +static guint gst_jack_ring_buffer_delay(GstRingBuffer * buf); + +#endif diff --git a/ext/jack/gstjackutil.c b/ext/jack/gstjackutil.c new file mode 100644 index 0000000000..cde84d8e85 --- /dev/null +++ b/ext/jack/gstjackutil.c @@ -0,0 +1,114 @@ +/* GStreamer Jack utility functions + * Copyright (C) 2010 Tristan Matthews + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gstjackutil.h" +#include + +static const GstAudioChannelPosition default_positions[8][8] = { + /* 1 channel */ + { + GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, + }, + /* 2 channels */ + { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + }, + /* 3 channels (2.1) */ + { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_LFE, /* or FRONT_CENTER for 3.0? */ + }, + /* 4 channels (4.0 or 3.1?) */ + { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, + }, + /* 5 channels */ + { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + }, + /* 6 channels */ + { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE, + }, + /* 7 channels */ + { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE, + GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, + }, + /* 8 channels */ + { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, + } +}; + + +/* if channels are less than or equal to 8, we set a default layout, + * otherwise set layout to an array of GST_AUDIO_CHANNEL_POSITION_NONE */ +void +gst_jack_set_layout_on_caps (GstCaps ** caps, gint channels) +{ + int c; + GValue pos = { 0 }; + GValue chanpos = { 0 }; + gst_caps_unref (*caps); + + if (channels <= 8) { + g_assert (channels >= 1); + gst_audio_set_channel_positions (gst_caps_get_structure (*caps, 0), + default_positions[channels - 1]); + } else { + g_value_init (&chanpos, GST_TYPE_ARRAY); + g_value_init (&pos, GST_TYPE_AUDIO_CHANNEL_POSITION); + for (c = 0; c < channels; c++) { + g_value_set_enum (&pos, GST_AUDIO_CHANNEL_POSITION_NONE); + gst_value_array_append_value (&chanpos, &pos); + } + g_value_unset (&pos); + gst_structure_set_value (gst_caps_get_structure (*caps, 0), + "channel-positions", &chanpos); + g_value_unset (&chanpos); + } + gst_caps_ref (*caps); +} diff --git a/ext/jack/gstjackutil.h b/ext/jack/gstjackutil.h new file mode 100644 index 0000000000..e330afd5e5 --- /dev/null +++ b/ext/jack/gstjackutil.h @@ -0,0 +1,30 @@ +/* GStreamer + * Copyright (C) 2010 Tristan Matthews + * + * gstjackutil.h: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _GST_JACK_UTIL_H_ +#define _GST_JACK_UTIL_H_ + +#include + +void +gst_jack_set_layout_on_caps (GstCaps **caps, gint channels); + +#endif // _GST_JACK_UTIL_H_ diff --git a/ext/jpeg/gstjpegdec.c b/ext/jpeg/gstjpegdec.c index 0518783e99..71ae4b9aee 100644 --- a/ext/jpeg/gstjpegdec.c +++ b/ext/jpeg/gstjpegdec.c @@ -52,11 +52,13 @@ (((struct GstJpegDecSourceMgr*)((cinfo_ptr)->src))->dec) #define JPEG_DEFAULT_IDCT_METHOD JDCT_FASTEST +#define JPEG_DEFAULT_MAX_ERRORS 0 enum { PROP_0, - PROP_IDCT_METHOD + PROP_IDCT_METHOD, + PROP_MAX_ERRORS }; /* *INDENT-OFF* */ @@ -192,6 +194,21 @@ gst_jpeg_dec_class_init (GstJpegDecClass * klass) JPEG_DEFAULT_IDCT_METHOD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstJpegDec:max-errors + * + * Error out after receiving N consecutive decoding errors + * (-1 = never error out, 0 = automatic, 1 = fail on first error, etc.) + * + * Since: 0.10.27 + **/ + g_object_class_install_property (gobject_class, PROP_MAX_ERRORS, + g_param_spec_int ("max-errors", "Maximum Consecutive Decoding Errors", + "Error out after receiving N consecutive decoding errors " + "(-1 = never fail, 0 = automatic, 1 = fail on first error)", + -1, G_MAXINT, JPEG_DEFAULT_MAX_ERRORS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_jpeg_dec_change_state); @@ -199,6 +216,81 @@ gst_jpeg_dec_class_init (GstJpegDecClass * klass) GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE"); } +static void +gst_jpeg_dec_clear_error (GstJpegDec * dec) +{ + g_free (dec->error_msg); + dec->error_msg = NULL; + dec->error_line = 0; + dec->error_func = NULL; +} + +static void +gst_jpeg_dec_set_error_va (GstJpegDec * dec, const gchar * func, gint line, + const gchar * debug_msg_format, va_list args) +{ +#ifndef GST_DISABLE_GST_DEBUG + gst_debug_log_valist (GST_CAT_DEFAULT, GST_LEVEL_WARNING, __FILE__, func, + line, (GObject *) dec, debug_msg_format, args); +#endif + + g_free (dec->error_msg); + if (debug_msg_format) + dec->error_msg = g_strdup_vprintf (debug_msg_format, args); + else + dec->error_msg = NULL; + + dec->error_line = line; + dec->error_func = func; +} + +static void +gst_jpeg_dec_set_error (GstJpegDec * dec, const gchar * func, gint line, + const gchar * debug_msg_format, ...) +{ + va_list va; + + va_start (va, debug_msg_format); + gst_jpeg_dec_set_error_va (dec, func, line, debug_msg_format, va); + va_end (va); +} + +static GstFlowReturn +gst_jpeg_dec_post_error_or_warning (GstJpegDec * dec) +{ + GstFlowReturn ret; + int max_errors; + + ++dec->error_count; + max_errors = g_atomic_int_get (&dec->max_errors); + + if (max_errors < 0) { + ret = GST_FLOW_OK; + } else if (max_errors == 0) { + /* FIXME: do something more clever in "automatic mode" */ + if (dec->packetized) { + ret = (dec->error_count < 3) ? GST_FLOW_OK : GST_FLOW_ERROR; + } else { + ret = GST_FLOW_ERROR; + } + } else { + ret = (dec->error_count < max_errors) ? GST_FLOW_OK : GST_FLOW_ERROR; + } + + GST_INFO_OBJECT (dec, "decoding error %d/%d (%s)", dec->error_count, + max_errors, (ret == GST_FLOW_OK) ? "ignoring error" : "erroring out"); + + gst_element_message_full (GST_ELEMENT (dec), + (ret == GST_FLOW_OK) ? GST_MESSAGE_WARNING : GST_MESSAGE_ERROR, + GST_STREAM_ERROR, GST_STREAM_ERROR_DECODE, + g_strdup (_("Failed to decode JPEG image")), dec->error_msg, + __FILE__, dec->error_func, dec->error_line); + + dec->error_msg = NULL; + gst_jpeg_dec_clear_error (dec); + return ret; +} + static boolean gst_jpeg_dec_fill_input_buffer (j_decompress_ptr cinfo) { @@ -346,6 +438,7 @@ gst_jpeg_dec_init (GstJpegDec * dec) /* init properties */ dec->idct_method = JPEG_DEFAULT_IDCT_METHOD; + dec->max_errors = JPEG_DEFAULT_MAX_ERRORS; dec->adapter = gst_adapter_new (); } @@ -954,10 +1047,9 @@ gst_jpeg_dec_decode_direct (GstJpegDec * dec, guchar * base[3], format_not_supported: { - GST_ELEMENT_ERROR (dec, STREAM, DECODE, - (_("Failed to decode JPEG image")), - ("Unsupported subsampling schema: v_samp factors: %u %u %u", - v_samp[0], v_samp[1], v_samp[2])); + gst_jpeg_dec_set_error (dec, GST_FUNCTION, __LINE__, + "Unsupported subsampling schema: v_samp factors: %u %u %u", + v_samp[0], v_samp[1], v_samp[2]); return GST_FLOW_ERROR; } } @@ -1440,6 +1532,11 @@ again: goto drop_buffer; } + /* reset error count on successful decode */ + dec->error_count = 0; + + ++dec->good_count; + GST_LOG_OBJECT (dec, "pushing buffer (ts=%" GST_TIME_FORMAT ", dur=%" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf))); @@ -1452,6 +1549,11 @@ done: exit: + if (G_UNLIKELY (ret == GST_FLOW_ERROR)) { + jpeg_abort_decompress (&dec->cinfo); + ret = gst_jpeg_dec_post_error_or_warning (dec); + } + return ret; /* special cases */ @@ -1468,9 +1570,8 @@ need_more_data: /* ERRORS */ wrong_size: { - GST_ELEMENT_ERROR (dec, STREAM, DECODE, - ("Picture is too small or too big (%ux%u)", width, height), - ("Picture is too small or too big (%ux%u)", width, height)); + gst_jpeg_dec_set_error (dec, GST_FUNCTION, __LINE__, + "Picture is too small or too big (%ux%u)", width, height); ret = GST_FLOW_ERROR; goto done; } @@ -1480,8 +1581,9 @@ decode_error: dec->jerr.pub.format_message ((j_common_ptr) (&dec->cinfo), err_msg); - GST_ELEMENT_ERROR (dec, STREAM, DECODE, - (_("Failed to decode JPEG image")), ("Error #%u: %s", code, err_msg)); + gst_jpeg_dec_set_error (dec, GST_FUNCTION, __LINE__, + "Decode error #%u: %s", code, err_msg); + if (outbuf) { gst_buffer_unref (outbuf); outbuf = NULL; @@ -1507,9 +1609,8 @@ alloc_failed: jpeg_abort_decompress (&dec->cinfo); if (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_WRONG_STATE && ret != GST_FLOW_NOT_LINKED) { - GST_ELEMENT_ERROR (dec, STREAM, DECODE, - ("Buffer allocation failed, reason: %s", reason), - ("Buffer allocation failed, reason: %s", reason)); + gst_jpeg_dec_set_error (dec, GST_FUNCTION, __LINE__, + "Buffer allocation failed, reason: %s", reason); } goto exit; } @@ -1522,22 +1623,22 @@ drop_buffer: } components_not_supported: { - GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), - ("more components than supported: %d > 3", dec->cinfo.num_components)); + gst_jpeg_dec_set_error (dec, GST_FUNCTION, __LINE__, + "more components than supported: %d > 3", dec->cinfo.num_components); ret = GST_FLOW_ERROR; goto done; } unsupported_colorspace: { - GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), - ("Picture has unknown or unsupported colourspace")); + gst_jpeg_dec_set_error (dec, GST_FUNCTION, __LINE__, + "Picture has unknown or unsupported colourspace"); ret = GST_FLOW_ERROR; goto done; } invalid_yuvrgbgrayscale: { - GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), - ("Picture is corrupt or unhandled YUV/RGB/grayscale layout")); + gst_jpeg_dec_set_error (dec, GST_FUNCTION, __LINE__, + "Picture is corrupt or unhandled YUV/RGB/grayscale layout"); ret = GST_FLOW_ERROR; goto done; } @@ -1632,6 +1733,9 @@ gst_jpeg_dec_set_property (GObject * object, guint prop_id, case PROP_IDCT_METHOD: dec->idct_method = g_value_get_enum (value); break; + case PROP_MAX_ERRORS: + g_atomic_int_set (&dec->max_errors, g_value_get_int (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -1651,6 +1755,9 @@ gst_jpeg_dec_get_property (GObject * object, guint prop_id, GValue * value, case PROP_IDCT_METHOD: g_value_set_enum (value, dec->idct_method); break; + case PROP_MAX_ERRORS: + g_value_set_int (value, g_atomic_int_get (&dec->max_errors)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -1668,6 +1775,8 @@ gst_jpeg_dec_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: + dec->error_count = 0; + dec->good_count = 0; dec->framerate_numerator = 0; dec->framerate_denominator = 1; dec->caps_framerate_numerator = dec->caps_framerate_denominator = 0; diff --git a/ext/jpeg/gstjpegdec.h b/ext/jpeg/gstjpegdec.h index 7f1ea53a81..7daf7b6f23 100644 --- a/ext/jpeg/gstjpegdec.h +++ b/ext/jpeg/gstjpegdec.h @@ -113,6 +113,18 @@ struct _GstJpegDec { /* properties */ gint idct_method; + gint max_errors; /* ATOMIC */ + + /* current error (the message is the debug message) */ + gchar *error_msg; + int error_line; + const gchar *error_func; + + /* number of errors since start or last successfully decoded image */ + guint error_count; + + /* number of successfully decoded images since start */ + guint good_count; struct jpeg_decompress_struct cinfo; struct GstJpegDecErrorMgr jerr; diff --git a/ext/jpeg/gstjpegenc.c b/ext/jpeg/gstjpegenc.c index 8f8196417d..60e2e99bb3 100644 --- a/ext/jpeg/gstjpegenc.c +++ b/ext/jpeg/gstjpegenc.c @@ -345,7 +345,7 @@ gst_jpegenc_getcaps (GstPad * pad) /* we want to proxy properties like width, height and framerate from the other end of the element */ - othercaps = gst_pad_get_allowed_caps (jpegenc->srcpad); + othercaps = gst_pad_peer_get_caps_reffed (jpegenc->srcpad); if (othercaps == NULL || gst_caps_is_empty (othercaps) || gst_caps_is_any (othercaps)) { caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); @@ -520,11 +520,12 @@ gst_jpegenc_resync (GstJpegEnc * jpegenc) jpegenc->h_samp[i], jpegenc->v_samp[i]); jpegenc->cinfo.comp_info[i].h_samp_factor = jpegenc->h_samp[i]; jpegenc->cinfo.comp_info[i].v_samp_factor = jpegenc->v_samp[i]; - jpegenc->line[i] = g_realloc (jpegenc->line[i], - jpegenc->v_max_samp * DCTSIZE * sizeof (char *)); + g_free (jpegenc->line[i]); + jpegenc->line[i] = g_new (guchar *, jpegenc->v_max_samp * DCTSIZE); if (!jpegenc->planar) { for (j = 0; j < jpegenc->v_max_samp * DCTSIZE; j++) { - jpegenc->row[i][j] = g_realloc (jpegenc->row[i][j], width); + g_free (jpegenc->row[i][j]); + jpegenc->row[i][j] = g_malloc (width); jpegenc->line[i][j] = jpegenc->row[i][j]; } } diff --git a/ext/libcaca/gstcacasink.c b/ext/libcaca/gstcacasink.c index ee05b36cc4..1846c26d48 100644 --- a/ext/libcaca/gstcacasink.c +++ b/ext/libcaca/gstcacasink.c @@ -209,12 +209,14 @@ gst_cacasink_setcaps (GstBaseSink * basesink, GstCaps * caps) { GstCACASink *cacasink; GstStructure *structure; + gint endianness; cacasink = GST_CACASINK (basesink); structure = gst_caps_get_structure (caps, 0); gst_structure_get_int (structure, "width", &(cacasink->width)); gst_structure_get_int (structure, "height", &(cacasink->height)); + gst_structure_get_int (structure, "endianness", &endianness); gst_structure_get_int (structure, "bpp", (int *) &cacasink->bpp); gst_structure_get_int (structure, "red_mask", (int *) &cacasink->red_mask); gst_structure_get_int (structure, "green_mask", @@ -233,10 +235,16 @@ gst_cacasink_setcaps (GstBaseSink * basesink, GstCaps * caps) cacasink->blue_mask = GUINT32_FROM_BE (cacasink->blue_mask); } - else if (cacasink->bpp == 16 || cacasink->bpp == 15) { - cacasink->red_mask = GUINT16_FROM_BE (cacasink->red_mask); - cacasink->green_mask = GUINT16_FROM_BE (cacasink->green_mask); - cacasink->blue_mask = GUINT16_FROM_BE (cacasink->blue_mask); + else if (cacasink->bpp == 16) { + if (endianness == G_BIG_ENDIAN) { + cacasink->red_mask = GUINT16_FROM_BE (cacasink->red_mask); + cacasink->green_mask = GUINT16_FROM_BE (cacasink->green_mask); + cacasink->blue_mask = GUINT16_FROM_BE (cacasink->blue_mask); + } else { + cacasink->red_mask = GUINT16_FROM_LE (cacasink->red_mask); + cacasink->green_mask = GUINT16_FROM_LE (cacasink->green_mask); + cacasink->blue_mask = GUINT16_FROM_LE (cacasink->blue_mask); + } } if (cacasink->bitmap) { @@ -246,7 +254,7 @@ gst_cacasink_setcaps (GstBaseSink * basesink, GstCaps * caps) cacasink->bitmap = caca_create_bitmap (cacasink->bpp, cacasink->width, cacasink->height, - cacasink->width * cacasink->bpp / 8, + GST_ROUND_UP_4 (cacasink->width * cacasink->bpp / 8), cacasink->red_mask, cacasink->green_mask, cacasink->blue_mask, 0); if (!cacasink->bitmap) { diff --git a/ext/libpng/gstpngdec.c b/ext/libpng/gstpngdec.c index 47090e5efb..4e43dec393 100644 --- a/ext/libpng/gstpngdec.c +++ b/ext/libpng/gstpngdec.c @@ -82,11 +82,18 @@ gst_pngdec_get_type (void) return pngdec_type; } +/* FIXME remove this after -good depends on -base-0.10.33 */ +#ifdef GST_VIDEO_CAPS_ARGB_64 +#define CAPS GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_ARGB_64 +#else +#define CAPS GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_RGB +#endif + static GstStaticPadTemplate gst_pngdec_src_pad_template = - GST_STATIC_PAD_TEMPLATE ("src", +GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_RGB) + GST_STATIC_CAPS (CAPS) ); static GstStaticPadTemplate gst_pngdec_sink_pad_template = @@ -370,12 +377,10 @@ gst_pngdec_caps_create_and_set (GstPngDec * pngdec) /* Get bits per channel */ bpc = png_get_bit_depth (pngdec->png, pngdec->info); - - /* We don't handle 16 bits per color, strip down to 8 */ - if (bpc == 16) { - GST_LOG_OBJECT (pngdec, - "this is a 16 bits per channel PNG image, strip down to 8 bits"); - png_set_strip_16 (pngdec->png); + if (bpc > 8) { + /* Add alpha channel if 16-bit depth */ + png_set_add_alpha (pngdec->png, 0xffff, PNG_FILLER_BEFORE); + png_set_swap (pngdec->png); } /* Get Color type */ @@ -428,11 +433,11 @@ gst_pngdec_caps_create_and_set (GstPngDec * pngdec) switch (pngdec->color_type) { case PNG_COLOR_TYPE_RGB: GST_LOG_OBJECT (pngdec, "we have no alpha channel, depth is 24 bits"); - pngdec->bpp = 24; + pngdec->bpp = 3 * bpc; break; case PNG_COLOR_TYPE_RGB_ALPHA: GST_LOG_OBJECT (pngdec, "we have an alpha channel, depth is 32 bits"); - pngdec->bpp = 32; + pngdec->bpp = 4 * bpc; break; default: GST_ELEMENT_ERROR (pngdec, STREAM, NOT_IMPLEMENTED, (NULL), diff --git a/ext/pulse/pulsemixerctrl.c b/ext/pulse/pulsemixerctrl.c index be5b536797..220c8d0655 100644 --- a/ext/pulse/pulsemixerctrl.c +++ b/ext/pulse/pulsemixerctrl.c @@ -61,6 +61,8 @@ gst_pulsemixer_ctrl_sink_info_cb (pa_context * context, const pa_sink_info * i, int eol, void *userdata) { GstPulseMixerCtrl *c = userdata; + gboolean vol_chg = FALSE; + gboolean old_mute; /* Called from the background thread! */ @@ -90,8 +92,10 @@ gst_pulsemixer_ctrl_sink_info_cb (pa_context * context, const pa_sink_info * i, c->description = g_strdup (i->description); c->index = i->index; c->channel_map = i->channel_map; + vol_chg = !pa_cvolume_equal (&c->volume, &i->volume); c->volume = i->volume; - c->muted = ! !i->mute; + old_mute = c->muted; + c->muted = !!i->mute; c->type = GST_PULSEMIXER_SINK; if (c->track) { @@ -104,6 +108,19 @@ gst_pulsemixer_ctrl_sink_info_cb (pa_context * context, const pa_sink_info * i, c->operation_success = TRUE; pa_threaded_mainloop_signal (c->mainloop, 0); + + if (vol_chg && c->track) { + gint volumes[PA_CHANNELS_MAX]; + gint i; + for (i = 0; i < c->volume.channels; i++) + volumes[i] = (gint) (c->volume.values[i]); + GST_LOG_OBJECT (c->object, "Sending volume change notification"); + gst_mixer_volume_changed (GST_MIXER (c->object), c->track, volumes); + } + if ((c->muted != old_mute) && c->track) { + GST_LOG_OBJECT (c->object, "Sending mute toggled notification"); + gst_mixer_mute_toggled (GST_MIXER (c->object), c->track, c->muted); + } } static void @@ -111,6 +128,8 @@ gst_pulsemixer_ctrl_source_info_cb (pa_context * context, const pa_source_info * i, int eol, void *userdata) { GstPulseMixerCtrl *c = userdata; + gboolean vol_chg = FALSE; + gboolean old_mute; /* Called from the background thread! */ @@ -140,8 +159,10 @@ gst_pulsemixer_ctrl_source_info_cb (pa_context * context, c->description = g_strdup (i->description); c->index = i->index; c->channel_map = i->channel_map; + vol_chg = !pa_cvolume_equal (&c->volume, &i->volume); c->volume = i->volume; - c->muted = ! !i->mute; + old_mute = c->muted; + c->muted = !!i->mute; c->type = GST_PULSEMIXER_SOURCE; if (c->track) { @@ -154,6 +175,19 @@ gst_pulsemixer_ctrl_source_info_cb (pa_context * context, c->operation_success = TRUE; pa_threaded_mainloop_signal (c->mainloop, 0); + + if (vol_chg && c->track) { + gint volumes[PA_CHANNELS_MAX]; + gint i; + for (i = 0; i < c->volume.channels; i++) + volumes[i] = (gint) (c->volume.values[i]); + GST_LOG_OBJECT (c->object, "Sending volume change notification"); + gst_mixer_volume_changed (GST_MIXER (c->object), c->track, volumes); + } + if ((c->muted != old_mute) && c->track) { + GST_LOG_OBJECT (c->object, "Sending mute toggled notification"); + gst_mixer_mute_toggled (GST_MIXER (c->object), c->track, c->muted); + } } static void @@ -195,7 +229,7 @@ gst_pulsemixer_ctrl_success_cb (pa_context * context, int success, { GstPulseMixerCtrl *c = (GstPulseMixerCtrl *) userdata; - c->operation_success = ! !success; + c->operation_success = !!success; pa_threaded_mainloop_signal (c->mainloop, 0); } @@ -395,6 +429,8 @@ gst_pulsemixer_ctrl_new (GObject * object, const gchar * server, const gchar * device, GstPulseMixerType type) { GstPulseMixerCtrl *c = NULL; + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE ((object), + GST_TYPE_MIXER), c); GST_DEBUG_OBJECT (object, "new mixer ctrl for %s", device); c = g_new (GstPulseMixerCtrl, 1); @@ -596,3 +632,9 @@ gst_pulsemixer_ctrl_set_mute (GstPulseMixerCtrl * c, GstMixerTrack * track, pa_threaded_mainloop_unlock (c->mainloop); } + +GstMixerFlags +gst_pulsemixer_ctrl_get_mixer_flags (GstPulseMixerCtrl * mixer) +{ + return GST_MIXER_FLAG_AUTO_NOTIFICATIONS; +} diff --git a/ext/pulse/pulsemixerctrl.h b/ext/pulse/pulsemixerctrl.h index e6b67ad266..c1d1e85285 100644 --- a/ext/pulse/pulsemixerctrl.h +++ b/ext/pulse/pulsemixerctrl.h @@ -90,6 +90,7 @@ void gst_pulsemixer_ctrl_set_mute (GstPulseMixerCtrl * mixer, GstMixerTrack * track, gboolean mute); void gst_pulsemixer_ctrl_set_record (GstPulseMixerCtrl * mixer, GstMixerTrack * track, gboolean record); +GstMixerFlags gst_pulsemixer_ctrl_get_mixer_flags (GstPulseMixerCtrl * mixer); #define GST_IMPLEMENT_PULSEMIXER_CTRL_METHODS(Type, interface_as_function) \ static const GList* \ @@ -146,6 +147,16 @@ interface_as_function ## _set_mute (GstMixer * mixer, GstMixerTrack * track, \ gst_pulsemixer_ctrl_set_mute (this->mixer, track, mute); \ } \ +static GstMixerFlags \ +interface_as_function ## _get_mixer_flags (GstMixer * mixer) \ +{ \ + Type *this = (Type*) mixer; \ + \ + g_return_val_if_fail (this != NULL, GST_MIXER_FLAG_NONE); \ + g_return_val_if_fail (this->mixer != NULL, GST_MIXER_FLAG_NONE); \ + \ + return gst_pulsemixer_ctrl_get_mixer_flags (this->mixer); \ +} \ static void \ interface_as_function ## _mixer_interface_init (GstMixerClass * klass) \ { \ @@ -156,6 +167,7 @@ interface_as_function ## _mixer_interface_init (GstMixerClass * klass) klass->get_volume = interface_as_function ## _get_volume; \ klass->set_mute = interface_as_function ## _set_mute; \ klass->set_record = interface_as_function ## _set_record; \ + klass->get_mixer_flags = interface_as_function ## _get_mixer_flags; \ } G_END_DECLS diff --git a/ext/pulse/pulsesink.c b/ext/pulse/pulsesink.c index 2a9a108b2e..295d93f494 100644 --- a/ext/pulse/pulsesink.c +++ b/ext/pulse/pulsesink.c @@ -1092,7 +1092,7 @@ gst_pulseringbuffer_pause (GstRingBuffer * buf) GST_DEBUG_OBJECT (psink, "pausing and corking"); /* make sure the commit method stops writing */ pbuf->paused = TRUE; - res = gst_pulsering_set_corked (pbuf, TRUE, FALSE); + res = gst_pulsering_set_corked (pbuf, TRUE, TRUE); if (pbuf->in_commit) { /* we are waiting in a commit, signal */ GST_DEBUG_OBJECT (psink, "signal commit"); @@ -1339,11 +1339,11 @@ gst_pulseringbuffer_commit (GstRingBuffer * buf, guint64 * sample, towrite = out_samples * bps; - /* Only ever write segsize bytes at once. This will - * also limit the PA shm buffer to segsize + /* Only ever write bufsize bytes at once. This will + * also limit the PA shm buffer to bufsize */ - if (towrite > buf->spec.segsize) - towrite = buf->spec.segsize; + if (towrite > bufsize) + towrite = bufsize; if ((pbuf->m_writable < towrite) || (offset != pbuf->m_lastoffset)) { /* if no room left or discontinuity in offset, @@ -1392,9 +1392,9 @@ gst_pulseringbuffer_commit (GstRingBuffer * buf, guint64 * sample, } /* make sure we only buffer up latency-time samples */ - if (pbuf->m_writable > buf->spec.segsize) { + if (pbuf->m_writable > bufsize) { /* limit buffering to latency-time value */ - pbuf->m_writable = buf->spec.segsize; + pbuf->m_writable = bufsize; GST_LOG_OBJECT (psink, "Limiting buffering to %" G_GSIZE_FORMAT, pbuf->m_writable); @@ -1413,9 +1413,9 @@ gst_pulseringbuffer_commit (GstRingBuffer * buf, guint64 * sample, pbuf->m_writable); /* Just to make sure that we didn't get more than requested */ - if (pbuf->m_writable > buf->spec.segsize) { + if (pbuf->m_writable > bufsize) { /* limit buffering to latency-time value */ - pbuf->m_writable = buf->spec.segsize; + pbuf->m_writable = bufsize; } } @@ -1649,6 +1649,50 @@ write_failed: } } +/* write pending local samples, must be called with the mainloop lock */ +static void +gst_pulsering_flush (GstPulseRingBuffer * pbuf) +{ +#ifdef HAVE_PULSE_0_9_16 + GstPulseSink *psink; + + psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf)); + GST_DEBUG_OBJECT (psink, "entering flush"); + + /* flush the buffer if possible */ + if (pbuf->stream && (pbuf->m_data != NULL) && (pbuf->m_towrite > 0)) { +#ifndef GST_DISABLE_GST_DEBUG + gint bps; + + bps = (GST_RING_BUFFER_CAST (pbuf))->spec.bytes_per_sample; + GST_LOG_OBJECT (psink, + "flushing %u samples at offset %" G_GINT64_FORMAT, + (guint) pbuf->m_towrite / bps, pbuf->m_offset); +#endif + + if (pa_stream_write (pbuf->stream, (uint8_t *) pbuf->m_data, + pbuf->m_towrite, NULL, pbuf->m_offset, PA_SEEK_ABSOLUTE) < 0) { + goto write_failed; + } + + pbuf->m_towrite = 0; + pbuf->m_offset += pbuf->m_towrite; /* keep track of current offset */ + } + +done: + return; + + /* ERRORS */ +write_failed: + { + GST_ELEMENT_ERROR (psink, RESOURCE, FAILED, + ("pa_stream_write() failed: %s", + pa_strerror (pa_context_errno (pbuf->context))), (NULL)); + goto done; + } +#endif +} + static void gst_pulsesink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_pulsesink_get_property (GObject * object, guint prop_id, @@ -2579,6 +2623,39 @@ update_failed: } #endif +static void +gst_pulsesink_flush_ringbuffer (GstPulseSink * psink) +{ + GstPulseRingBuffer *pbuf; + + pa_threaded_mainloop_lock (mainloop); + + pbuf = GST_PULSERING_BUFFER_CAST (GST_BASE_AUDIO_SINK (psink)->ringbuffer); + + if (pbuf == NULL || pbuf->stream == NULL) + goto no_buffer; + + gst_pulsering_flush (pbuf); + + /* Uncork if we haven't already (happens when waiting to get enough data + * to send out the first time) */ + if (pbuf->corked) + gst_pulsering_set_corked (pbuf, FALSE, FALSE); + + /* We're not interested if this operation failed or not */ +unlock: + pa_threaded_mainloop_unlock (mainloop); + + return; + + /* ERRORS */ +no_buffer: + { + GST_DEBUG_OBJECT (psink, "we have no ringbuffer"); + goto unlock; + } +} + static gboolean gst_pulsesink_event (GstBaseSink * sink, GstEvent * event) { @@ -2626,6 +2703,9 @@ gst_pulsesink_event (GstBaseSink * sink, GstEvent * event) break; } + case GST_EVENT_EOS: + gst_pulsesink_flush_ringbuffer (pulsesink); + break; default: ; } diff --git a/ext/pulse/pulsesrc.c b/ext/pulse/pulsesrc.c index 0509bfd6e1..06fb2069c2 100644 --- a/ext/pulse/pulsesrc.c +++ b/ext/pulse/pulsesrc.c @@ -61,6 +61,7 @@ enum PROP_SERVER, PROP_DEVICE, PROP_DEVICE_NAME, + PROP_CLIENT, PROP_STREAM_PROPERTIES, PROP_LAST }; @@ -246,6 +247,20 @@ gst_pulsesrc_class_init (GstPulseSrcClass * klass) "Human-readable name of the sound device", DEFAULT_DEVICE_NAME, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * GstPulseSink:client + * + * The PulseAudio client name to use. + * + * Since: 0.10.27 + */ + g_object_class_install_property (gobject_class, + PROP_CLIENT, + g_param_spec_string ("client", "Client", + "The PulseAudio client_name_to_use", gst_pulse_client_name (), + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + /** * GstPulseSrc:stream-properties * @@ -275,6 +290,7 @@ gst_pulsesrc_init (GstPulseSrc * pulsesrc, GstPulseSrcClass * klass) { pulsesrc->server = NULL; pulsesrc->device = NULL; + pulsesrc->client_name = gst_pulse_client_name (); pulsesrc->device_description = NULL; pulsesrc->context = NULL; @@ -340,6 +356,7 @@ gst_pulsesrc_finalize (GObject * object) g_free (pulsesrc->server); g_free (pulsesrc->device); + g_free (pulsesrc->client_name); if (pulsesrc->properties) gst_structure_free (pulsesrc->properties); @@ -463,6 +480,15 @@ gst_pulsesrc_set_property (GObject * object, g_free (pulsesrc->device); pulsesrc->device = g_value_dup_string (value); break; + case PROP_CLIENT: + g_free (pulsesrc->client_name); + if (!g_value_get_string (value)) { + GST_WARNING_OBJECT (pulsesrc, + "Empty PulseAudio client name not allowed. Resetting to default value"); + pulsesrc->client_name = gst_pulse_client_name (); + } else + pulsesrc->client_name = g_value_dup_string (value); + break; case PROP_STREAM_PROPERTIES: if (pulsesrc->properties) gst_structure_free (pulsesrc->properties); @@ -495,6 +521,9 @@ gst_pulsesrc_get_property (GObject * object, case PROP_DEVICE_NAME: g_value_take_string (value, gst_pulsesrc_device_description (pulsesrc)); break; + case PROP_CLIENT: + g_value_set_string (value, pulsesrc->client_name); + break; case PROP_STREAM_PROPERTIES: gst_value_set_structure (value, pulsesrc->properties); break; @@ -599,7 +628,6 @@ static gboolean gst_pulsesrc_open (GstAudioSrc * asrc) { GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (asrc); - gchar *name = gst_pulse_client_name (); pa_threaded_mainloop_lock (pulsesrc->mainloop); @@ -610,7 +638,7 @@ gst_pulsesrc_open (GstAudioSrc * asrc) if (!(pulsesrc->context = pa_context_new (pa_threaded_mainloop_get_api (pulsesrc->mainloop), - name))) { + pulsesrc->client_name))) { GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Failed to create context"), (NULL)); goto unlock_and_fail; @@ -649,7 +677,6 @@ gst_pulsesrc_open (GstAudioSrc * asrc) pa_threaded_mainloop_unlock (pulsesrc->mainloop); - g_free (name); return TRUE; /* ERRORS */ @@ -659,7 +686,6 @@ unlock_and_fail: pa_threaded_mainloop_unlock (pulsesrc->mainloop); - g_free (name); return FALSE; } } diff --git a/ext/pulse/pulsesrc.h b/ext/pulse/pulsesrc.h index fb5006d6f6..6e6322b9c3 100644 --- a/ext/pulse/pulsesrc.h +++ b/ext/pulse/pulsesrc.h @@ -55,7 +55,7 @@ struct _GstPulseSrc { GstAudioSrc src; - gchar *server, *device; + gchar *server, *device, *client_name; pa_threaded_mainloop *mainloop; @@ -68,7 +68,6 @@ struct _GstPulseSrc size_t read_buffer_length; gchar *device_description; - GstPulseMixerCtrl *mixer; GstPulseProbe *probe; diff --git a/ext/soup/gstsouphttpsrc.c b/ext/soup/gstsouphttpsrc.c index 4d77984564..819fdecdec 100644 --- a/ext/soup/gstsouphttpsrc.c +++ b/ext/soup/gstsouphttpsrc.c @@ -135,6 +135,7 @@ static gboolean gst_soup_http_src_get_size (GstBaseSrc * bsrc, guint64 * size); static gboolean gst_soup_http_src_is_seekable (GstBaseSrc * bsrc); static gboolean gst_soup_http_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment); +static gboolean gst_soup_http_src_query (GstBaseSrc * bsrc, GstQuery * query); static gboolean gst_soup_http_src_unlock (GstBaseSrc * bsrc); static gboolean gst_soup_http_src_unlock_stop (GstBaseSrc * bsrc); static gboolean gst_soup_http_src_set_location (GstSoupHTTPSrc * src, @@ -307,6 +308,7 @@ gst_soup_http_src_class_init (GstSoupHTTPSrcClass * klass) gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_soup_http_src_is_seekable); gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_soup_http_src_do_seek); + gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_soup_http_src_query); gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_soup_http_src_create); } @@ -1394,6 +1396,28 @@ gst_soup_http_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment) return TRUE; } +static gboolean +gst_soup_http_src_query (GstBaseSrc * bsrc, GstQuery * query) +{ + GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (bsrc); + gboolean ret; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_URI: + gst_query_set_uri (query, src->location); + ret = TRUE; + break; + default: + ret = FALSE; + break; + } + + if (!ret) + ret = GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query); + + return ret; +} + static gboolean gst_soup_http_src_set_location (GstSoupHTTPSrc * src, const gchar * uri) { diff --git a/gst-plugins-good.doap b/gst-plugins-good.doap index 5e1323d39b..23dbfac28d 100644 --- a/gst-plugins-good.doap +++ b/gst-plugins-good.doap @@ -20,7 +20,7 @@ the plug-in code, LGPL or LGPL-compatible for the supporting library). - + C @@ -32,6 +32,17 @@ the plug-in code, LGPL or LGPL-compatible for the supporting library). + + + 0.10.27 + 0.10 + Some Kind of Temporal Blend + 2011-01-21 + + + + + 0.10.26 diff --git a/gst-plugins-good.spec.in b/gst-plugins-good.spec.in index 6cdd96c4c9..88dc858ace 100644 --- a/gst-plugins-good.spec.in +++ b/gst-plugins-good.spec.in @@ -147,6 +147,7 @@ rm -rf $RPM_BUILD_ROOT @USE_LIBCACA_TRUE@%{_libdir}/gstreamer-%{majorminor}/libgstcacasink.so @USE_ESD_TRUE@%{_libdir}/gstreamer-%{majorminor}/libgstesd.so @USE_FLAC_TRUE@%{_libdir}/gstreamer-%{majorminor}/libgstflac.so +@USE_JACK_TRUE@%{_libdir}/gstreamer-%{majorminor}/libgstjack.so @USE_JPEG_TRUE@%{_libdir}/gstreamer-%{majorminor}/libgstjpeg.so @USE_LIBPNG_TRUE@%{_libdir}/gstreamer-%{majorminor}/libgstpng.so @USE_OSS_TRUE@%{_libdir}/gstreamer-%{majorminor}/libgstossaudio.so diff --git a/gst/alpha/gstalpha.c b/gst/alpha/gstalpha.c index fd9b851d68..e9c29d5715 100644 --- a/gst/alpha/gstalpha.c +++ b/gst/alpha/gstalpha.c @@ -1425,7 +1425,7 @@ gst_alpha_set_planar_yuv_ayuv (const guint8 * src, guint8 * dest, gint width, break; default: g_assert_not_reached (); - break; + return; } if (alpha->in_sdtv == alpha->out_sdtv) { @@ -1544,7 +1544,7 @@ gst_alpha_chroma_key_planar_yuv_ayuv (const guint8 * src, guint8 * dest, break; default: g_assert_not_reached (); - break; + return; } if (alpha->in_sdtv == alpha->out_sdtv) { @@ -1685,7 +1685,7 @@ gst_alpha_set_planar_yuv_argb (const guint8 * src, guint8 * dest, gint width, break; default: g_assert_not_reached (); - break; + return; } memcpy (matrix, @@ -1793,7 +1793,7 @@ gst_alpha_chroma_key_planar_yuv_argb (const guint8 * src, guint8 * dest, break; default: g_assert_not_reached (); - break; + return; } memcpy (matrix, diff --git a/gst/audiofx/audiochebband.c b/gst/audiofx/audiochebband.c index 037304c58e..89a950c23e 100644 --- a/gst/audiofx/audiochebband.c +++ b/gst/audiofx/audiochebband.c @@ -229,7 +229,7 @@ generate_biquad_coefficients (GstAudioChebBand * filter, /* Calculate pole location for lowpass at frequency 1 */ { - gdouble angle = (M_PI / 2.0) * (2.0 * p - 1) / np; + gdouble angle = (G_PI / 2.0) * (2.0 * p - 1) / np; rp = -sin (angle); ip = cos (angle); @@ -266,7 +266,7 @@ generate_biquad_coefficients (GstAudioChebBand * filter, /* Calculate zero location for frequency 1 on the * unit circle for type 2 */ if (type == 2) { - gdouble angle = M_PI / (np * 2.0) + ((p - 1) * M_PI) / (np); + gdouble angle = G_PI / (np * 2.0) + ((p - 1) * G_PI) / (np); gdouble mag2; iz = cos (angle); @@ -339,10 +339,10 @@ generate_biquad_coefficients (GstAudioChebBand * filter, gdouble a, b, d; gdouble alpha, beta; gdouble w0 = - 2.0 * M_PI * (filter->lower_frequency / + 2.0 * G_PI * (filter->lower_frequency / GST_AUDIO_FILTER (filter)->format.rate); gdouble w1 = - 2.0 * M_PI * (filter->upper_frequency / + 2.0 * G_PI * (filter->upper_frequency / GST_AUDIO_FILTER (filter)->format.rate); if (filter->mode == MODE_BAND_PASS) { @@ -498,10 +498,10 @@ generate_coefficients (GstAudioChebBand * filter) /* gain is H(wc), wc = center frequency */ gdouble w1 = - 2.0 * M_PI * (filter->lower_frequency / + 2.0 * G_PI * (filter->lower_frequency / GST_AUDIO_FILTER (filter)->format.rate); gdouble w2 = - 2.0 * M_PI * (filter->upper_frequency / + 2.0 * G_PI * (filter->upper_frequency / GST_AUDIO_FILTER (filter)->format.rate); gdouble w0 = (w2 + w1) / 2.0; gdouble zr = cos (w0), zi = sin (w0); @@ -530,10 +530,10 @@ generate_coefficients (GstAudioChebBand * filter) np + 1, 1.0, 0.0))); { gdouble w1 = - 2.0 * M_PI * (filter->lower_frequency / + 2.0 * G_PI * (filter->lower_frequency / GST_AUDIO_FILTER (filter)->format.rate); gdouble w2 = - 2.0 * M_PI * (filter->upper_frequency / + 2.0 * G_PI * (filter->upper_frequency / GST_AUDIO_FILTER (filter)->format.rate); gdouble w0 = (w2 + w1) / 2.0; gdouble zr, zi; diff --git a/gst/audiofx/audiocheblimit.c b/gst/audiofx/audiocheblimit.c index 02cbc14bf4..8d51fbbf66 100644 --- a/gst/audiofx/audiocheblimit.c +++ b/gst/audiofx/audiocheblimit.c @@ -222,7 +222,7 @@ generate_biquad_coefficients (GstAudioChebLimit * filter, /* Calculate pole location for lowpass at frequency 1 */ { - gdouble angle = (M_PI / 2.0) * (2.0 * p - 1) / np; + gdouble angle = (G_PI / 2.0) * (2.0 * p - 1) / np; rp = -sin (angle); ip = cos (angle); @@ -259,7 +259,7 @@ generate_biquad_coefficients (GstAudioChebLimit * filter, /* Calculate zero location for frequency 1 on the * unit circle for type 2 */ if (type == 2) { - gdouble angle = M_PI / (np * 2.0) + ((p - 1) * M_PI) / (np); + gdouble angle = G_PI / (np * 2.0) + ((p - 1) * G_PI) / (np); gdouble mag2; iz = cos (angle); @@ -324,7 +324,7 @@ generate_biquad_coefficients (GstAudioChebLimit * filter, { gdouble k, d; gdouble omega = - 2.0 * M_PI * (filter->cutoff / GST_AUDIO_FILTER (filter)->format.rate); + 2.0 * G_PI * (filter->cutoff / GST_AUDIO_FILTER (filter)->format.rate); if (filter->mode == MODE_LOW_PASS) k = sin ((1.0 - omega) / 2.0) / sin ((1.0 + omega) / 2.0); @@ -456,7 +456,7 @@ generate_coefficients (GstAudioChebLimit * filter) #ifndef GST_DISABLE_GST_DEBUG { gdouble wc = - 2.0 * M_PI * (filter->cutoff / + 2.0 * G_PI * (filter->cutoff / GST_AUDIO_FILTER (filter)->format.rate); gdouble zr = cos (wc), zi = sin (wc); diff --git a/gst/audiofx/audiokaraoke.c b/gst/audiofx/audiokaraoke.c index aeda8d3b82..c6fb93ee49 100644 --- a/gst/audiofx/audiokaraoke.c +++ b/gst/audiofx/audiokaraoke.c @@ -179,8 +179,8 @@ update_filter (GstAudioKaraoke * filter, gint rate) if (rate == 0) return; - C = exp (-2 * M_PI * filter->filter_width / rate); - B = -4 * C / (1 + C) * cos (2 * M_PI * filter->filter_band / rate); + C = exp (-2 * G_PI * filter->filter_width / rate); + B = -4 * C / (1 + C) * cos (2 * G_PI * filter->filter_band / rate); A = sqrt (1 - B * B / (4 * C)) * (1 - C); filter->A = A; diff --git a/gst/audiofx/audiowsincband.c b/gst/audiofx/audiowsincband.c index 4b76a16a36..c6e2a0d1ae 100644 --- a/gst/audiofx/audiowsincband.c +++ b/gst/audiofx/audiowsincband.c @@ -255,7 +255,7 @@ gst_audio_wsincband_build_kernel (GstAudioWSincBand * self) (self->mode == MODE_BAND_PASS) ? "band-pass" : "band-reject"); /* fill the lp kernel */ - w = 2 * M_PI * (self->lower_frequency / GST_AUDIO_FILTER (self)->format.rate); + w = 2 * G_PI * (self->lower_frequency / GST_AUDIO_FILTER (self)->format.rate); kernel_lp = g_new (gdouble, len); for (i = 0; i < len; ++i) { if (i == len / 2) @@ -265,11 +265,11 @@ gst_audio_wsincband_build_kernel (GstAudioWSincBand * self) / (i - len / 2); /* Windowing */ if (self->window == WINDOW_HAMMING) - kernel_lp[i] *= (0.54 - 0.46 * cos (2 * M_PI * i / len)); + kernel_lp[i] *= (0.54 - 0.46 * cos (2 * G_PI * i / len)); else kernel_lp[i] *= - (0.42 - 0.5 * cos (2 * M_PI * i / len) + - 0.08 * cos (4 * M_PI * i / len)); + (0.42 - 0.5 * cos (2 * G_PI * i / len) + + 0.08 * cos (4 * G_PI * i / len)); } /* normalize for unity gain at DC */ @@ -280,7 +280,7 @@ gst_audio_wsincband_build_kernel (GstAudioWSincBand * self) kernel_lp[i] /= sum; /* fill the hp kernel */ - w = 2 * M_PI * (self->upper_frequency / GST_AUDIO_FILTER (self)->format.rate); + w = 2 * G_PI * (self->upper_frequency / GST_AUDIO_FILTER (self)->format.rate); kernel_hp = g_new (gdouble, len); for (i = 0; i < len; ++i) { if (i == len / 2) @@ -290,11 +290,11 @@ gst_audio_wsincband_build_kernel (GstAudioWSincBand * self) / (i - len / 2); /* Windowing */ if (self->window == WINDOW_HAMMING) - kernel_hp[i] *= (0.54 - 0.46 * cos (2 * M_PI * i / len)); + kernel_hp[i] *= (0.54 - 0.46 * cos (2 * G_PI * i / len)); else kernel_hp[i] *= - (0.42 - 0.5 * cos (2 * M_PI * i / len) + - 0.08 * cos (4 * M_PI * i / len)); + (0.42 - 0.5 * cos (2 * G_PI * i / len) + + 0.08 * cos (4 * G_PI * i / len)); } /* normalize for unity gain at DC */ diff --git a/gst/audiofx/audiowsinclimit.c b/gst/audiofx/audiowsinclimit.c index 4b79e930be..046f1ca268 100644 --- a/gst/audiofx/audiowsinclimit.c +++ b/gst/audiofx/audiowsinclimit.c @@ -239,7 +239,7 @@ gst_audio_wsinclimit_build_kernel (GstAudioWSincLimit * self) (self->mode == MODE_LOW_PASS) ? "low-pass" : "high-pass"); /* fill the kernel */ - w = 2 * M_PI * (self->cutoff / GST_AUDIO_FILTER (self)->format.rate); + w = 2 * G_PI * (self->cutoff / GST_AUDIO_FILTER (self)->format.rate); kernel = g_new (gdouble, len); @@ -250,10 +250,10 @@ gst_audio_wsinclimit_build_kernel (GstAudioWSincLimit * self) kernel[i] = sin (w * (i - len / 2)) / (i - len / 2); /* windowing */ if (self->window == WINDOW_HAMMING) - kernel[i] *= (0.54 - 0.46 * cos (2 * M_PI * i / len)); + kernel[i] *= (0.54 - 0.46 * cos (2 * G_PI * i / len)); else - kernel[i] *= (0.42 - 0.5 * cos (2 * M_PI * i / len) + - 0.08 * cos (4 * M_PI * i / len)); + kernel[i] *= (0.42 - 0.5 * cos (2 * G_PI * i / len) + + 0.08 * cos (4 * G_PI * i / len)); } /* normalize for unity gain at DC */ diff --git a/gst/avi/gstavidemux.c b/gst/avi/gstavidemux.c index 5e4c1be984..011eb7d1c5 100644 --- a/gst/avi/gstavidemux.c +++ b/gst/avi/gstavidemux.c @@ -696,7 +696,7 @@ gst_avi_demux_seek_streams_index (GstAviDemux * avi, guint64 offset, GstAviIndexEntry *entry; gint i; gint64 val, min = offset; - guint index; + guint index = 0; for (i = 0; i < avi->num_streams; i++) { stream = &avi->stream[i]; @@ -1270,6 +1270,11 @@ gst_avi_demux_parse_superindex (GstAviDemux * avi, GST_DEBUG_OBJECT (avi, "got %d indexes", num); + /* this can't work out well ... */ + if (num > G_MAXUINT32 >> 1 || bpe < 8) { + goto invalid_params; + } + indexes = g_new (guint64, num + 1); for (i = 0; i < num; i++) { if (size < 24 + bpe * (i + 1)) @@ -1293,6 +1298,14 @@ too_small: gst_buffer_unref (buf); return FALSE; } +invalid_params: + { + GST_ERROR_OBJECT (avi, "invalid index parameters (num = %d, bpe = %d)", + num, bpe); + if (buf) + gst_buffer_unref (buf); + return FALSE; + } } /* add an entry to the index of a stream. @num should be an estimate of the @@ -1858,6 +1871,11 @@ gst_avi_demux_expose_streams (GstAviDemux * avi, gboolean force) GST_LOG_OBJECT (avi, "Added pad %s with caps %" GST_PTR_FORMAT, GST_PAD_NAME (stream->pad), GST_PAD_CAPS (stream->pad)); gst_element_add_pad ((GstElement *) avi, stream->pad); + + if (avi->element_index) + gst_index_get_writer_id (avi->element_index, + GST_OBJECT_CAST (stream->pad), &stream->index_id); + stream->exposed = TRUE; if (avi->main_stream == -1) avi->main_stream = i; @@ -2015,10 +2033,12 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf) res = gst_riff_parse_strf_auds (element, sub, &stream->strf.auds, &stream->extradata); + sub = NULL; + if (!res) + break; stream->is_vbr = (stream->strh->samplesize == 0) && stream->strh->scale > 1 && stream->strf.auds->blockalign != 1; - sub = NULL; GST_DEBUG_OBJECT (element, "marking audio as VBR:%d, res %d", stream->is_vbr, res); /* we need these or we have no way to come up with timestamps */ @@ -2265,10 +2285,6 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf) GST_DEBUG_FUNCPTR (gst_avi_demux_src_convert)); #endif - if (avi->element_index) - gst_index_get_writer_id (avi->element_index, GST_OBJECT_CAST (stream->pad), - &stream->index_id); - stream->num = avi->num_streams; stream->start_entry = 0; @@ -3980,7 +3996,7 @@ gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment) GstAviStream *stream; seek_time = segment->last_stop; - keyframe = ! !(segment->flags & GST_SEEK_FLAG_KEY_UNIT); + keyframe = !!(segment->flags & GST_SEEK_FLAG_KEY_UNIT); GST_DEBUG_OBJECT (avi, "seek to: %" GST_TIME_FORMAT " keyframe seeking:%d", GST_TIME_ARGS (seek_time), keyframe); @@ -4246,7 +4262,7 @@ avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad, GstEvent * event) gst_segment_set_seek (&seeksegment, rate, format, flags, cur_type, cur, stop_type, stop, &update); - keyframe = ! !(flags & GST_SEEK_FLAG_KEY_UNIT); + keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT); cur = seeksegment.last_stop; GST_DEBUG_OBJECT (avi, @@ -4417,9 +4433,10 @@ swap_line (guint8 * d1, guint8 * d2, guint8 * tmp, gint bytes) #define gst_avi_demux_is_uncompressed(fourcc) \ - (fourcc == GST_RIFF_DIB || \ - fourcc == GST_RIFF_rgb || \ - fourcc == GST_RIFF_RGB || fourcc == GST_RIFF_RAW) + (fourcc && \ + (fourcc == GST_RIFF_DIB || \ + fourcc == GST_RIFF_rgb || \ + fourcc == GST_RIFF_RGB || fourcc == GST_RIFF_RAW)) /* * Invert DIB buffers... Takes existing buffer and @@ -4484,13 +4501,15 @@ gst_avi_demux_add_assoc (GstAviDemux * avi, GstAviStream * stream, GST_LOG_OBJECT (avi, "adding association %" GST_TIME_FORMAT "-> %" G_GUINT64_FORMAT, GST_TIME_ARGS (timestamp), offset); gst_index_add_association (avi->element_index, avi->index_id, - keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT : GST_ASSOCIATION_FLAG_NONE, - GST_FORMAT_TIME, timestamp, GST_FORMAT_BYTES, offset, NULL); - /* well, current_total determines TIME and entry DEFAULT (frame #) ... */ + keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT : + GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, timestamp, + GST_FORMAT_BYTES, offset, NULL); + /* current_entry is DEFAULT (frame #) */ gst_index_add_association (avi->element_index, stream->index_id, - GST_ASSOCIATION_FLAG_NONE, - GST_FORMAT_TIME, stream->current_total, GST_FORMAT_BYTES, offset, - GST_FORMAT_DEFAULT, stream->current_entry, NULL); + keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT : + GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, timestamp, + GST_FORMAT_BYTES, offset, GST_FORMAT_DEFAULT, stream->current_entry, + NULL); } } diff --git a/gst/avi/gstavimux.c b/gst/avi/gstavimux.c index 83dbbbd2f2..d0d2f0ecc6 100644 --- a/gst/avi/gstavimux.c +++ b/gst/avi/gstavimux.c @@ -41,7 +41,7 @@ * ! 'audio/x-raw-int,rate=44100,channels=2' ! queue ! mux. \ * avimux name=mux ! filesink location=test.avi * ]| This will create an .AVI file containing an uncompressed video stream - * with a test picture and an uncompressed audio stream containing a + * with a test picture and an uncompressed audio stream containing a * test sound. * |[ * gst-launch videotestsrc num-buffers=250 \ @@ -61,6 +61,7 @@ #endif #include "gst/gst-i18n-plugin.h" +#include #include #include @@ -964,6 +965,10 @@ gst_avi_mux_request_new_pad (GstElement * element, GstPad *newpad; GstAviPad *avipad; GstElementClass *klass; + gchar *name = NULL; + const gchar *pad_name = NULL; + GstPadSetCapsFunction setcapsfunc = NULL; + gint pad_id; g_return_val_if_fail (templ != NULL, NULL); @@ -978,22 +983,23 @@ gst_avi_mux_request_new_pad (GstElement * element, klass = GST_ELEMENT_GET_CLASS (element); - if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) { - gchar *name; + /* FIXME-0.11: use %d instead of %02d for pad_names */ - /* setup pad */ - name = g_strdup_printf ("audio_%02d", avimux->audio_pads); - GST_DEBUG_OBJECT (avimux, "adding new pad: %s", name); - newpad = gst_pad_new_from_template (templ, name); - g_free (name); - gst_pad_set_setcaps_function (newpad, - GST_DEBUG_FUNCPTR (gst_avi_mux_audsink_set_caps)); + if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) { + /* don't mix named and unnamed pads, if the pad already exists we fail when + * trying to add it */ + if (req_name != NULL && sscanf (req_name, "audio_%02d", &pad_id) == 1) { + pad_name = req_name; + } else { + name = g_strdup_printf ("audio_%02d", avimux->audio_pads++); + pad_name = name; + } + setcapsfunc = GST_DEBUG_FUNCPTR (gst_avi_mux_audsink_set_caps); /* init pad specific data */ avipad = g_malloc0 (sizeof (GstAviAudioPad)); avipad->is_video = FALSE; avipad->hdr.type = GST_MAKE_FOURCC ('a', 'u', 'd', 's'); - avimux->audio_pads++; /* audio goes last */ avimux->sinkpads = g_slist_append (avimux->sinkpads, avipad); } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) { @@ -1001,23 +1007,27 @@ gst_avi_mux_request_new_pad (GstElement * element, * some video info goes in a single avi header -and therefore mux struct- * so video restricted to one stream */ if (avimux->video_pads > 0) - return NULL; + goto too_many_video_pads; + /* setup pad */ - GST_DEBUG_OBJECT (avimux, "adding new pad: video_00"); - newpad = gst_pad_new_from_template (templ, "video_00"); - gst_pad_set_setcaps_function (newpad, - GST_DEBUG_FUNCPTR (gst_avi_mux_vidsink_set_caps)); - avipad = g_malloc0 (sizeof (GstAviVideoPad)); + pad_name = "video_00"; + avimux->video_pads++; + setcapsfunc = GST_DEBUG_FUNCPTR (gst_avi_mux_vidsink_set_caps); /* init pad specific data */ + avipad = g_malloc0 (sizeof (GstAviVideoPad)); avipad->is_video = TRUE; avipad->hdr.type = GST_MAKE_FOURCC ('v', 'i', 'd', 's'); - avimux->video_pads++; /* video goes first */ avimux->sinkpads = g_slist_prepend (avimux->sinkpads, avipad); } else goto wrong_template; + newpad = gst_pad_new_from_template (templ, pad_name); + gst_pad_set_setcaps_function (newpad, setcapsfunc); + + g_free (name); + avipad->collect = gst_collect_pads_add_pad (avimux->collect, newpad, sizeof (GstAviCollectData)); ((GstAviCollectData *) (avipad->collect))->avipad = avipad; @@ -1028,7 +1038,10 @@ gst_avi_mux_request_new_pad (GstElement * element, gst_pad_set_event_function (newpad, GST_DEBUG_FUNCPTR (gst_avi_mux_handle_event)); - gst_element_add_pad (element, newpad); + if (!gst_element_add_pad (element, newpad)) + goto pad_add_failed; + + GST_DEBUG_OBJECT (newpad, "Added new request pad"); return newpad; @@ -1048,6 +1061,17 @@ wrong_template: g_warning ("avimux: this is not our template!\n"); return NULL; } +too_many_video_pads: + { + GST_WARNING_OBJECT (avimux, "Can only have one video stream"); + return NULL; + } +pad_add_failed: + { + GST_WARNING_OBJECT (avimux, "Adding the new pad '%s' failed", pad_name); + gst_object_unref (newpad); + return NULL; + } } static void @@ -1673,6 +1697,7 @@ gst_avi_mux_start_file (GstAviMux * avimux) GstFlowReturn res; GstBuffer *header; GSList *node; + GstCaps *caps; avimux->total_data = 0; avimux->total_frames = 0; @@ -1715,6 +1740,10 @@ gst_avi_mux_start_file (GstAviMux * avimux) } } + caps = gst_caps_copy (gst_pad_get_pad_template_caps (avimux->srcpad)); + gst_pad_set_caps (avimux->srcpad, caps); + gst_caps_unref (caps); + /* let downstream know we think in BYTES and expect to do seeking later on */ gst_pad_push_event (avimux->srcpad, gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0)); diff --git a/gst/avi/gstavisubtitle.c b/gst/avi/gstavisubtitle.c index 38771c38f6..10c4fafe7d 100644 --- a/gst/avi/gstavisubtitle.c +++ b/gst/avi/gstavisubtitle.c @@ -333,12 +333,20 @@ gst_avi_subtitle_class_init (GstAviSubtitleClass * klass) static void gst_avi_subtitle_init (GstAviSubtitle * self, GstAviSubtitleClass * klass) { + GstCaps *caps; + self->src = gst_pad_new_from_static_template (&src_template, "src"); gst_element_add_pad (GST_ELEMENT (self), self->src); self->sink = gst_pad_new_from_static_template (&sink_template, "sink"); gst_pad_set_chain_function (self->sink, GST_DEBUG_FUNCPTR (gst_avi_subtitle_chain)); + + caps = gst_static_pad_template_get_caps (&src_template); + gst_pad_set_caps (self->src, caps); + gst_caps_unref (caps); + + gst_pad_use_fixed_caps (self->src); gst_element_add_pad (GST_ELEMENT (self), self->sink); self->subfile = NULL; diff --git a/gst/debugutils/Makefile.am b/gst/debugutils/Makefile.am index 1f423b65ea..d905413c1f 100644 --- a/gst/debugutils/Makefile.am +++ b/gst/debugutils/Makefile.am @@ -46,8 +46,6 @@ libgstdebug_la_SOURCES = \ cpureport.c \ testplugin.c -# negotiation.c - libgstdebug_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) libgstdebug_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) libgstdebug_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) diff --git a/gst/debugutils/gstcapsdebug.c b/gst/debugutils/gstcapsdebug.c index f4a1743cee..3806bae6a8 100644 --- a/gst/debugutils/gstcapsdebug.c +++ b/gst/debugutils/gstcapsdebug.c @@ -26,6 +26,9 @@ #include #include "gstcapsdebug.h" +GST_DEBUG_CATEGORY_STATIC (gst_caps_debug_debug); +#define GST_CAT_DEFAULT gst_caps_debug_debug + /* prototypes */ @@ -67,7 +70,12 @@ GST_STATIC_PAD_TEMPLATE ("src", /* class initialization */ -GST_BOILERPLATE (GstCapsDebug, gst_caps_debug, GstElement, GST_TYPE_ELEMENT); +#define DEBUG_INIT(bla) \ + GST_DEBUG_CATEGORY_INIT (gst_caps_debug_debug, "capsdebug", 0, \ + "debug category for capsdebug element"); + +GST_BOILERPLATE_FULL (GstCapsDebug, gst_caps_debug, GstElement, + GST_TYPE_ELEMENT, DEBUG_INIT); static void gst_caps_debug_base_init (gpointer g_class) @@ -103,8 +111,7 @@ gst_caps_debug_init (GstCapsDebug * capsdebug, { capsdebug->srcpad = - gst_pad_new_from_template (gst_static_pad_template_get - (&gst_caps_debug_src_template), "src"); + gst_pad_new_from_static_template (&gst_caps_debug_src_template, "src"); gst_pad_set_getcaps_function (capsdebug->srcpad, GST_DEBUG_FUNCPTR (gst_caps_debug_getcaps)); gst_pad_set_acceptcaps_function (capsdebug->srcpad, @@ -112,8 +119,7 @@ gst_caps_debug_init (GstCapsDebug * capsdebug, gst_element_add_pad (GST_ELEMENT (capsdebug), capsdebug->srcpad); capsdebug->sinkpad = - gst_pad_new_from_template (gst_static_pad_template_get - (&gst_caps_debug_sink_template), "sink"); + gst_pad_new_from_static_template (&gst_caps_debug_sink_template, "sink"); gst_pad_set_chain_function (capsdebug->sinkpad, GST_DEBUG_FUNCPTR (gst_caps_debug_sink_chain)); gst_pad_set_bufferalloc_function (capsdebug->sinkpad, diff --git a/gst/debugutils/gsttaginject.c b/gst/debugutils/gsttaginject.c index 9197ad58a4..a5e78fe9aa 100644 --- a/gst/debugutils/gsttaginject.c +++ b/gst/debugutils/gsttaginject.c @@ -168,6 +168,9 @@ gst_tag_inject_set_property (GObject * object, guint prop_id, if (!(self->tags = gst_structure_from_string (structure, NULL))) { GST_WARNING ("unparsable taglist = '%s'", structure); } + + /* make sure that tags will be send */ + self->tags_sent = FALSE; g_free (structure); break; } diff --git a/gst/debugutils/negotiation.c b/gst/debugutils/negotiation.c deleted file mode 100644 index 176653ff45..0000000000 --- a/gst/debugutils/negotiation.c +++ /dev/null @@ -1,282 +0,0 @@ -/* - * GStreamer - * Copyright (C) 1999-2001 Erik Walthinsen - * Copyright (C) 2002 David A. Schleef - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include - -#include -#include -#include - - -#define GST_TYPE_NEGOTIATION \ - (gst_gst_negotiation_get_type()) -#define GST_NEGOTIATION(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_NEGOTIATION,GstNegotiation)) -#define GST_NEGOTIATION_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_NEGOTIATION,GstNegotiation)) -#define GST_IS_NEGOTIATION(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_NEGOTIATION)) -#define GST_IS_NEGOTIATION_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_NEGOTIATION)) - -typedef struct _GstNegotiation GstNegotiation; -typedef struct _GstNegotiationClass GstNegotiationClass; - -struct _GstNegotiation -{ - GstElement element; - - GstPad *sinkpad, *srcpad; - - GstCaps *caps; -}; - -struct _GstNegotiationClass -{ - GstElementClass parent_class; -}; - -GType gst_gst_negotiation_get_type (void); - -/* Filter signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; - -enum -{ - ARG_0, - ARG_ALLOWED_CAPS -}; - -static GstStaticPadTemplate gst_negotiation_sink_factory = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -static GstStaticPadTemplate gst_negotiation_src_factory = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -static void gst_negotiation_base_init (gpointer g_class); -static void gst_negotiation_class_init (GstNegotiationClass * klass); -static void gst_negotiation_init (GstNegotiation * filter); - -static GstCaps *gst_negotiation_getcaps (GstPad * pad); -static GstPadLinkReturn gst_negotiation_pad_link (GstPad * pad, - const GstCaps * caps); - -static void gst_negotiation_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_negotiation_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static void gst_negotiation_update_caps (GstNegotiation * negotiation); -static void gst_negotiation_chain (GstPad * pad, GstData * _data); - -static GstElementClass *parent_class = NULL; - -GType -gst_gst_negotiation_get_type (void) -{ - static GType plugin_type = 0; - - if (!plugin_type) { - static const GTypeInfo plugin_info = { - sizeof (GstNegotiationClass), - gst_negotiation_base_init, - NULL, - (GClassInitFunc) gst_negotiation_class_init, - NULL, - NULL, - sizeof (GstNegotiation), - 0, - (GInstanceInitFunc) gst_negotiation_init, - }; - - plugin_type = g_type_register_static (GST_TYPE_ELEMENT, - "GstNegotiation", &plugin_info, 0); - } - return plugin_type; -} - -static void -gst_negotiation_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&gst_negotiation_sink_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&gst_negotiation_src_factory)); - gst_element_class_set_details_simple (element_class, "Negotiation", - "Testing", - "This element acts like identity, except that one can control how " - "negotiation works", "David A. Schleef "); -} - -static void -gst_negotiation_class_init (GstNegotiationClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->set_property = gst_negotiation_set_property; - gobject_class->get_property = gst_negotiation_get_property; - - g_object_class_install_property (gobject_class, ARG_ALLOWED_CAPS, - g_param_spec_boxed ("allowed-caps", "Caps", - "The range of formats allowed by " "this element's peers", - GST_TYPE_CAPS, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); -} - -static void -gst_negotiation_init (GstNegotiation * filter) -{ - filter->sinkpad = - gst_pad_new_from_static_template (&gst_negotiation_sink_factory, "sink"); - gst_pad_set_getcaps_function (filter->sinkpad, gst_negotiation_getcaps); - gst_pad_set_link_function (filter->sinkpad, gst_negotiation_pad_link); - filter->srcpad = - gst_pad_new_from_static_template (&gst_negotiation_src_factory, "src"); - gst_pad_set_getcaps_function (filter->srcpad, gst_negotiation_getcaps); - gst_pad_set_link_function (filter->srcpad, gst_negotiation_pad_link); - - gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad); - gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad); - gst_pad_set_chain_function (filter->sinkpad, gst_negotiation_chain); -} - -static GstCaps * -gst_negotiation_getcaps (GstPad * pad) -{ - GstNegotiation *negotiation = GST_NEGOTIATION (gst_pad_get_parent (pad)); - GstPad *otherpad; - GstCaps *caps; - - otherpad = (pad == negotiation->sinkpad) ? negotiation->srcpad : - negotiation->sinkpad; - - caps = gst_pad_get_allowed_caps (otherpad); - - GST_ERROR ("getcaps called on %" GST_PTR_FORMAT ", returning %" - GST_PTR_FORMAT, pad, caps); - - gst_negotiation_update_caps (negotiation); - gst_object_unref (negotiation); - - return caps; -} - -static GstPadLinkReturn -gst_negotiation_pad_link (GstPad * pad, const GstCaps * caps) -{ - GstNegotiation *negotiation = GST_NEGOTIATION (gst_pad_get_parent (pad)); - GstPad *otherpad; - GstPadLinkReturn ret; - - otherpad = (pad == negotiation->sinkpad) ? negotiation->srcpad : - negotiation->sinkpad; - - ret = gst_pad_try_set_caps (otherpad, caps); - - GST_ERROR ("pad_link called on %" GST_PTR_FORMAT " with caps %" - GST_PTR_FORMAT ", returning %d", pad, caps, ret); - gst_object_unref (negotiation); - - return ret; -} - -static void -gst_negotiation_update_caps (GstNegotiation * negotiation) -{ - GstCaps *srccaps; - GstCaps *sinkcaps; - GstCaps *icaps; - - srccaps = gst_pad_get_allowed_caps (negotiation->srcpad); - sinkcaps = gst_pad_get_allowed_caps (negotiation->sinkpad); - - icaps = gst_caps_intersect (srccaps, sinkcaps); - gst_caps_free (srccaps); - gst_caps_free (sinkcaps); - - gst_caps_replace (&negotiation->caps, icaps); - g_object_notify (G_OBJECT (negotiation), "allowed-caps"); - GST_DEBUG ("notify %" GST_PTR_FORMAT, icaps); -} - -static void -gst_negotiation_chain (GstPad * pad, GstData * _data) -{ - GstNegotiation *negotiation = GST_NEGOTIATION (gst_pad_get_parent (pad)); - - gst_pad_push (negotiation->srcpad, _data); - gst_object_unref (negotiation); -} - -static void -gst_negotiation_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstNegotiation *filter; - - g_return_if_fail (GST_IS_NEGOTIATION (object)); - filter = GST_NEGOTIATION (object); - - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_negotiation_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstNegotiation *filter; - - g_return_if_fail (GST_IS_NEGOTIATION (object)); - filter = GST_NEGOTIATION (object); - - switch (prop_id) { - case ARG_ALLOWED_CAPS: - g_value_set_boxed (value, filter->caps); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} diff --git a/gst/deinterlace/gstdeinterlace.c b/gst/deinterlace/gstdeinterlace.c index 7d235e443c..ce76f720c8 100644 --- a/gst/deinterlace/gstdeinterlace.c +++ b/gst/deinterlace/gstdeinterlace.c @@ -52,7 +52,7 @@ GST_DEBUG_CATEGORY_STATIC (deinterlace_debug); /* Properties */ #define DEFAULT_MODE GST_DEINTERLACE_MODE_AUTO -#define DEFAULT_METHOD GST_DEINTERLACE_GREEDY_H +#define DEFAULT_METHOD GST_DEINTERLACE_LINEAR #define DEFAULT_FIELDS GST_DEINTERLACE_ALL #define DEFAULT_FIELD_LAYOUT GST_DEINTERLACE_LAYOUT_AUTO @@ -160,34 +160,24 @@ gst_deinterlace_modes_get_type (void) return deinterlace_modes_type; } +#define DEINTERLACE_CAPS \ + GST_VIDEO_CAPS_YUV ("{ AYUV, Y444, YUY2, YVYU, UYVY, Y42B, I420, YV12, Y41B, NV12, NV21 }") ";" \ + GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_ABGR ";" \ + GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_BGRA ";" \ + GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";" \ + GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx ";" \ + GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR + static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_YUV ("Y444") - ";" GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("YVYU") ";" - GST_VIDEO_CAPS_YUV ("UYVY") ";" - GST_VIDEO_CAPS_YUV ("Y42B") ";" GST_VIDEO_CAPS_YUV ("I420") ";" - GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("Y41B") ";" - GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_ABGR ";" - GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_BGRA ";" - GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";" - GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx ";" - GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR) + GST_STATIC_CAPS (DEINTERLACE_CAPS) ); static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_YUV ("Y444") - ";" GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("YVYU") ";" - GST_VIDEO_CAPS_YUV ("UYVY") ";" - GST_VIDEO_CAPS_YUV ("Y42B") ";" GST_VIDEO_CAPS_YUV ("I420") ";" - GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("Y41B") ";" - GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_ABGR ";" - GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_BGRA ";" - GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";" - GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx ";" - GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR) + GST_STATIC_CAPS (DEINTERLACE_CAPS) ); static void gst_deinterlace_finalize (GObject * self); @@ -296,6 +286,7 @@ gst_deinterlace_set_method (GstDeinterlace * self, GstDeinterlaceMethods method) self->height)) { GST_DEBUG_OBJECT (self, "Using method %d", i); method_type = tmp; + method = i; break; } } @@ -369,7 +360,7 @@ gst_deinterlace_base_init (gpointer klass) gst_element_class_set_details_simple (element_class, "Deinterlacer", - "Filter/Video", + "Filter/Effect/Video/Deinterlace", "Deinterlace Methods ported from DScaler/TvTime", "Martin Eikermann , " "Sebastian Dröge "); @@ -573,8 +564,6 @@ gst_deinterlace_init (GstDeinterlace * self, GstDeinterlaceClass * klass) GST_DEBUG_FUNCPTR (gst_deinterlace_src_query)); gst_pad_set_getcaps_function (self->srcpad, GST_DEBUG_FUNCPTR (gst_deinterlace_getcaps)); - gst_pad_set_setcaps_function (self->srcpad, - GST_DEBUG_FUNCPTR (gst_deinterlace_setcaps)); gst_element_add_pad (GST_ELEMENT (self), self->srcpad); self->mode = DEFAULT_MODE; @@ -593,6 +582,16 @@ gst_deinterlace_reset_history (GstDeinterlace * self, gboolean drop_all) { gint i; + if (!drop_all) { + GST_DEBUG_OBJECT (self, "Flushing history (count %d)", self->history_count); + while (self->history_count > 0) { + if (gst_deinterlace_output_frame (self, TRUE) != GST_FLOW_OK) { + /* Encountered error, or flushing -> skip and drop all remaining */ + drop_all = TRUE; + break; + } + } + } if (drop_all) { GST_DEBUG_OBJECT (self, "Resetting history (count %d)", self->history_count); @@ -603,10 +602,6 @@ gst_deinterlace_reset_history (GstDeinterlace * self, gboolean drop_all) self->field_history[i].buf = NULL; } } - } else { - GST_DEBUG_OBJECT (self, "Flushing history (count %d)", self->history_count); - while (self->history_count > 0) - gst_deinterlace_output_frame (self, TRUE); } memset (self->field_history, 0, GST_DEINTERLACE_MAX_FIELD_HISTORY * sizeof (GstDeinterlaceField)); @@ -648,14 +643,6 @@ gst_deinterlace_reset (GstDeinterlace * self) gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); - if (self->sink_caps) - gst_caps_unref (self->sink_caps); - self->sink_caps = NULL; - - if (self->src_caps) - gst_caps_unref (self->src_caps); - self->src_caps = NULL; - if (self->request_caps) gst_caps_unref (self->request_caps); self->request_caps = NULL; @@ -773,8 +760,8 @@ gst_deinterlace_pop_history (GstDeinterlace * self) self->history_count--; - GST_DEBUG_OBJECT (self, "Returning buffer: %" GST_TIME_FORMAT - " with duration %" GST_TIME_FORMAT " and size %u", + GST_DEBUG_OBJECT (self, "Returning buffer: %p %" GST_TIME_FORMAT + " with duration %" GST_TIME_FORMAT " and size %u", buffer, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)), GST_BUFFER_SIZE (buffer)); @@ -1005,13 +992,15 @@ gst_deinterlace_output_frame (GstDeinterlace * self, gboolean flushing) GST_DEBUG_OBJECT (self, "deinterlacing top field"); /* create new buffer */ - ret = gst_pad_alloc_buffer (self->srcpad, - GST_BUFFER_OFFSET_NONE, self->frame_size, self->src_caps, &outbuf); + ret = + gst_pad_alloc_buffer (self->srcpad, GST_BUFFER_OFFSET_NONE, + self->frame_size, GST_PAD_CAPS (self->srcpad), &outbuf); if (ret != GST_FLOW_OK) return ret; - if (self->src_caps != GST_BUFFER_CAPS (outbuf) && - !gst_caps_is_equal (self->src_caps, GST_BUFFER_CAPS (outbuf))) { + if (GST_PAD_CAPS (self->srcpad) != GST_BUFFER_CAPS (outbuf) + && !gst_caps_is_equal (GST_PAD_CAPS (self->srcpad), + GST_BUFFER_CAPS (outbuf))) { gst_caps_replace (&self->request_caps, GST_BUFFER_CAPS (outbuf)); GST_DEBUG_OBJECT (self, "Upstream wants new caps %" GST_PTR_FORMAT, self->request_caps); @@ -1022,7 +1011,7 @@ gst_deinterlace_output_frame (GstDeinterlace * self, gboolean flushing) if (!outbuf) return GST_FLOW_ERROR; - gst_buffer_set_caps (outbuf, self->src_caps); + gst_buffer_set_caps (outbuf, GST_PAD_CAPS (self->srcpad)); } g_return_val_if_fail (self->history_count - 1 - @@ -1083,13 +1072,15 @@ gst_deinterlace_output_frame (GstDeinterlace * self, gboolean flushing) GST_DEBUG_OBJECT (self, "deinterlacing bottom field"); /* create new buffer */ - ret = gst_pad_alloc_buffer (self->srcpad, - GST_BUFFER_OFFSET_NONE, self->frame_size, self->src_caps, &outbuf); + ret = + gst_pad_alloc_buffer (self->srcpad, GST_BUFFER_OFFSET_NONE, + self->frame_size, GST_PAD_CAPS (self->srcpad), &outbuf); if (ret != GST_FLOW_OK) return ret; - if (self->src_caps != GST_BUFFER_CAPS (outbuf) && - !gst_caps_is_equal (self->src_caps, GST_BUFFER_CAPS (outbuf))) { + if (GST_PAD_CAPS (self->srcpad) != GST_BUFFER_CAPS (outbuf) + && !gst_caps_is_equal (GST_PAD_CAPS (self->srcpad), + GST_BUFFER_CAPS (outbuf))) { gst_caps_replace (&self->request_caps, GST_BUFFER_CAPS (outbuf)); GST_DEBUG_OBJECT (self, "Upstream wants new caps %" GST_PTR_FORMAT, self->request_caps); @@ -1100,7 +1091,7 @@ gst_deinterlace_output_frame (GstDeinterlace * self, gboolean flushing) if (!outbuf) return GST_FLOW_ERROR; - gst_buffer_set_caps (outbuf, self->src_caps); + gst_buffer_set_caps (outbuf, GST_PAD_CAPS (self->srcpad)); } g_return_val_if_fail (self->history_count - 1 - @@ -1394,10 +1385,7 @@ gst_deinterlace_setcaps (GstPad * pad, GstCaps * caps) { gboolean res = TRUE; GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad)); - GstPad *otherpad; - GstCaps *othercaps; - - otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad; + GstCaps *srccaps; res = gst_video_format_parse_caps (caps, &self->format, &self->width, @@ -1413,53 +1401,45 @@ gst_deinterlace_setcaps (GstPad * pad, GstCaps * caps) if (!self->passthrough && self->fields == GST_DEINTERLACE_ALL) { gint fps_n = self->fps_n, fps_d = self->fps_d; - if (!gst_fraction_double (&fps_n, &fps_d, otherpad != self->srcpad)) + if (!gst_fraction_double (&fps_n, &fps_d, FALSE)) goto invalid_caps; - othercaps = gst_caps_copy (caps); + srccaps = gst_caps_copy (caps); - gst_caps_set_simple (othercaps, "framerate", GST_TYPE_FRACTION, fps_n, + gst_caps_set_simple (srccaps, "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL); } else { - othercaps = gst_caps_ref (caps); + srccaps = gst_caps_ref (caps); } - if (otherpad == self->srcpad && self->mode != GST_DEINTERLACE_MODE_DISABLED) { - othercaps = gst_caps_make_writable (othercaps); - gst_caps_set_simple (othercaps, "interlaced", G_TYPE_BOOLEAN, FALSE, NULL); + if (self->mode != GST_DEINTERLACE_MODE_DISABLED) { + srccaps = gst_caps_make_writable (srccaps); + gst_caps_set_simple (srccaps, "interlaced", G_TYPE_BOOLEAN, FALSE, NULL); } gst_deinterlace_reset_history (self, FALSE); - if (!gst_pad_set_caps (otherpad, othercaps)) + if (!gst_pad_set_caps (self->srcpad, srccaps)) goto caps_not_accepted; self->frame_size = gst_video_format_get_size (self->format, self->width, self->height); - if (self->fields == GST_DEINTERLACE_ALL && otherpad == self->srcpad) - self->field_duration = - gst_util_uint64_scale (GST_SECOND, self->fps_d, self->fps_n); - else + if (G_LIKELY (self->fps_n != 0)) { self->field_duration = gst_util_uint64_scale (GST_SECOND, self->fps_d, 2 * self->fps_n); - - if (pad == self->sinkpad) { - gst_caps_replace (&self->sink_caps, caps); - gst_caps_replace (&self->src_caps, othercaps); } else { - gst_caps_replace (&self->src_caps, caps); - gst_caps_replace (&self->sink_caps, othercaps); + self->field_duration = 0; } gst_deinterlace_set_method (self, self->method_id); gst_deinterlace_method_setup (self->method, self->format, self->width, self->height); - GST_DEBUG_OBJECT (pad, "Set caps: %" GST_PTR_FORMAT, caps); - GST_DEBUG_OBJECT (pad, "Other caps: %" GST_PTR_FORMAT, othercaps); + GST_DEBUG_OBJECT (pad, "Sink caps: %" GST_PTR_FORMAT, caps); + GST_DEBUG_OBJECT (pad, "Src caps: %" GST_PTR_FORMAT, srccaps); - gst_caps_unref (othercaps); + gst_caps_unref (srccaps); done: @@ -1473,8 +1453,8 @@ invalid_caps: caps_not_accepted: res = FALSE; - GST_ERROR_OBJECT (pad, "Caps not accepted: %" GST_PTR_FORMAT, othercaps); - gst_caps_unref (othercaps); + GST_ERROR_OBJECT (pad, "Caps not accepted: %" GST_PTR_FORMAT, srccaps); + gst_caps_unref (srccaps); goto done; } diff --git a/gst/deinterlace/gstdeinterlace.h b/gst/deinterlace/gstdeinterlace.h index 6641da2087..df996f8425 100644 --- a/gst/deinterlace/gstdeinterlace.h +++ b/gst/deinterlace/gstdeinterlace.h @@ -129,10 +129,6 @@ struct _GstDeinterlace gdouble proportion; GstClockTime earliest_time; - /* Upstream negotiation stuff */ - GstCaps *sink_caps; - GstCaps *src_caps; - GstCaps *request_caps; gboolean reconfigure; diff --git a/gst/deinterlace/gstdeinterlacemethod.c b/gst/deinterlace/gstdeinterlacemethod.c index ac653dff89..fe5996b52e 100644 --- a/gst/deinterlace/gstdeinterlacemethod.c +++ b/gst/deinterlace/gstdeinterlacemethod.c @@ -69,6 +69,10 @@ gst_deinterlace_method_supported_impl (GstDeinterlaceMethodClass * klass, return (klass->deinterlace_frame_y41b != NULL); case GST_VIDEO_FORMAT_AYUV: return (klass->deinterlace_frame_ayuv != NULL); + case GST_VIDEO_FORMAT_NV12: + return (klass->deinterlace_frame_nv12 != NULL); + case GST_VIDEO_FORMAT_NV21: + return (klass->deinterlace_frame_nv21 != NULL); case GST_VIDEO_FORMAT_ARGB: case GST_VIDEO_FORMAT_xRGB: return (klass->deinterlace_frame_argb != NULL); @@ -152,6 +156,12 @@ gst_deinterlace_method_setup_impl (GstDeinterlaceMethod * self, case GST_VIDEO_FORMAT_AYUV: self->deinterlace_frame = klass->deinterlace_frame_ayuv; break; + case GST_VIDEO_FORMAT_NV12: + self->deinterlace_frame = klass->deinterlace_frame_nv12; + break; + case GST_VIDEO_FORMAT_NV21: + self->deinterlace_frame = klass->deinterlace_frame_nv21; + break; case GST_VIDEO_FORMAT_ARGB: case GST_VIDEO_FORMAT_xRGB: self->deinterlace_frame = klass->deinterlace_frame_argb; @@ -268,6 +278,12 @@ gst_deinterlace_simple_method_supported (GstDeinterlaceMethodClass * mklass, case GST_VIDEO_FORMAT_AYUV: return (klass->interpolate_scanline_ayuv != NULL && klass->copy_scanline_ayuv != NULL); + case GST_VIDEO_FORMAT_NV12: + return (klass->interpolate_scanline_nv12 != NULL + && klass->copy_scanline_nv12 != NULL); + case GST_VIDEO_FORMAT_NV21: + return (klass->interpolate_scanline_nv21 != NULL + && klass->copy_scanline_nv21 != NULL); case GST_VIDEO_FORMAT_I420: case GST_VIDEO_FORMAT_YV12: case GST_VIDEO_FORMAT_Y444: @@ -307,132 +323,84 @@ gst_deinterlace_simple_method_deinterlace_frame_packed (GstDeinterlaceMethod * GstDeinterlaceSimpleMethod *self = GST_DEINTERLACE_SIMPLE_METHOD (method); GstDeinterlaceMethodClass *dm_class = GST_DEINTERLACE_METHOD_GET_CLASS (self); GstDeinterlaceScanlineData scanlines; - guint8 *out = GST_BUFFER_DATA (outbuf); - const guint8 *field0 = NULL, *field1 = NULL, *field2 = NULL, *field3 = NULL; + guint8 *dest; + const guint8 *field0, *field1, *field2, *field3; gint cur_field_idx = history_count - dm_class->fields_required; guint cur_field_flags = history[cur_field_idx].flags; - gint line; - gint field_height = self->parent.frame_height / 2; - gint row_stride = self->parent.row_stride[0]; - gint field_stride = self->parent.row_stride[0] * 2; + gint i; + gint frame_height = self->parent.frame_height; + gint stride = self->parent.row_stride[0]; g_assert (self->interpolate_scanline_packed != NULL); g_assert (self->copy_scanline_packed != NULL); + dest = GST_BUFFER_DATA (outbuf); field0 = GST_BUFFER_DATA (history[cur_field_idx].buf); - if (history[cur_field_idx].flags & PICTURE_INTERLACED_BOTTOM) - field0 += row_stride; g_assert (dm_class->fields_required <= 4); if (dm_class->fields_required >= 2) { field1 = GST_BUFFER_DATA (history[cur_field_idx + 1].buf); - if (history[cur_field_idx + 1].flags & PICTURE_INTERLACED_BOTTOM) - field1 += row_stride; + } else { + field1 = NULL; } if (dm_class->fields_required >= 3) { field2 = GST_BUFFER_DATA (history[cur_field_idx + 2].buf); - if (history[cur_field_idx + 2].flags & PICTURE_INTERLACED_BOTTOM) - field2 += row_stride; + } else { + field2 = NULL; } if (dm_class->fields_required >= 4) { field3 = GST_BUFFER_DATA (history[cur_field_idx + 3].buf); - if (history[cur_field_idx + 3].flags & PICTURE_INTERLACED_BOTTOM) - field3 += row_stride; + } else { + field3 = NULL; } - if (cur_field_flags == PICTURE_INTERLACED_BOTTOM) { - /* double the first scanline of the bottom field */ - memcpy (out, field0, row_stride); - out += row_stride; - } - - memcpy (out, field0, row_stride); - out += row_stride; - - for (line = 2; line <= field_height; line++) { +#define CLAMP_LOW(i) (((i)<0) ? (i+2) : (i)) +#define CLAMP_HI(i) (((i)>=(frame_height)) ? (i-2) : (i)) +#define LINE(x,i) ((x) + CLAMP_HI(CLAMP_LOW(i)) * (stride)) +#define LINE2(x,i) ((x) ? LINE(x,i) : NULL) + for (i = 0; i < frame_height; i++) { memset (&scanlines, 0, sizeof (scanlines)); scanlines.bottom_field = (cur_field_flags == PICTURE_INTERLACED_BOTTOM); - /* interp. scanline */ - scanlines.t0 = field0; - scanlines.b0 = field0 + field_stride; + if (!((i & 1) ^ scanlines.bottom_field)) { + /* copying */ + scanlines.tt0 = LINE2 (field0, (i - 2 >= 0) ? i - 2 : i); + scanlines.m0 = LINE2 (field0, i); + scanlines.bb0 = LINE2 (field0, (i + 2 < frame_height ? i + 2 : i)); - if (field1 != NULL) { - scanlines.tt1 = field1; - scanlines.m1 = field1 + field_stride; - scanlines.bb1 = field1 + field_stride * 2; - field1 += field_stride; + scanlines.t1 = LINE2 (field1, i - 1); + scanlines.b1 = LINE2 (field1, i + 1); + + scanlines.tt2 = LINE2 (field2, (i - 2 >= 0) ? i - 2 : i); + scanlines.m2 = LINE2 (field2, i); + scanlines.bb2 = LINE2 (field2, (i + 2 < frame_height ? i + 2 : i)); + + scanlines.t3 = LINE2 (field3, i - 1); + scanlines.b3 = LINE2 (field3, i + 1); + + self->copy_scanline_packed (self, LINE (dest, i), &scanlines); + } else { + /* interpolating */ + scanlines.t0 = LINE2 (field0, i - 1); + scanlines.b0 = LINE2 (field0, i + 1); + + scanlines.tt1 = LINE2 (field1, (i - 2 >= 0) ? i - 2 : i); + scanlines.m1 = LINE2 (field1, i); + scanlines.bb1 = LINE2 (field1, (i + 2 < frame_height ? i + 2 : i)); + + scanlines.t2 = LINE2 (field2, i - 1); + scanlines.b2 = LINE2 (field2, i + 1); + + scanlines.tt3 = LINE2 (field3, (i - 2 >= 0) ? i - 2 : i); + scanlines.m3 = LINE2 (field3, i); + scanlines.bb3 = LINE2 (field3, (i + 2 < frame_height ? i + 2 : i)); + + self->interpolate_scanline_packed (self, LINE (dest, i), &scanlines); } - - if (field2 != NULL) { - scanlines.t2 = field2; - scanlines.b2 = field2 + field_stride; - } - - if (field3 != NULL) { - scanlines.tt3 = field3; - scanlines.m3 = field3 + field_stride; - scanlines.bb3 = field3 + field_stride * 2; - field3 += field_stride; - } - - /* set valid data for corner cases */ - if (line == 2) { - scanlines.tt1 = scanlines.bb1; - scanlines.tt3 = scanlines.bb3; - } else if (line == field_height) { - scanlines.bb1 = scanlines.tt1; - scanlines.bb3 = scanlines.tt3; - } - - self->interpolate_scanline_packed (self, out, &scanlines); - out += row_stride; - - memset (&scanlines, 0, sizeof (scanlines)); - scanlines.bottom_field = (cur_field_flags == PICTURE_INTERLACED_BOTTOM); - - /* copy a scanline */ - scanlines.tt0 = field0; - scanlines.m0 = field0 + field_stride; - scanlines.bb0 = field0 + field_stride * 2; - field0 += field_stride; - - if (field1 != NULL) { - scanlines.t1 = field1; - scanlines.b1 = field1 + field_stride; - } - - if (field2 != NULL) { - scanlines.tt2 = field2; - scanlines.m2 = field2 + field_stride; - scanlines.bb2 = field2 + field_stride * 2; - field2 += field_stride; - } - - if (field3 != NULL) { - scanlines.t3 = field3; - scanlines.b3 = field3 + field_stride; - } - - /* set valid data for corner cases */ - if (line == field_height) { - scanlines.bb0 = scanlines.tt0; - scanlines.b1 = scanlines.t1; - scanlines.bb2 = scanlines.tt2; - scanlines.b3 = scanlines.t3; - } - - self->copy_scanline_packed (self, out, &scanlines); - out += row_stride; - } - - if (cur_field_flags == PICTURE_INTERLACED_TOP) { - /* double the last scanline of the top field */ - memcpy (out, field0, row_stride); } } @@ -483,111 +451,59 @@ gst_deinterlace_simple_method_copy_scanline_planar_v (GstDeinterlaceSimpleMethod static void gst_deinterlace_simple_method_deinterlace_frame_planar_plane - (GstDeinterlaceSimpleMethod * self, guint8 * out, const guint8 * field0, + (GstDeinterlaceSimpleMethod * self, guint8 * dest, const guint8 * field0, const guint8 * field1, const guint8 * field2, const guint8 * field3, guint cur_field_flags, gint plane, GstDeinterlaceSimpleMethodFunction copy_scanline, GstDeinterlaceSimpleMethodFunction interpolate_scanline) { GstDeinterlaceScanlineData scanlines; - gint line; - gint field_height = self->parent.height[plane] / 2; - gint row_stride = self->parent.row_stride[plane]; - gint field_stride = self->parent.row_stride[plane] * 2; + gint i; + gint frame_height = self->parent.height[plane]; + gint stride = self->parent.row_stride[plane]; g_assert (interpolate_scanline != NULL); g_assert (copy_scanline != NULL); - if (cur_field_flags == PICTURE_INTERLACED_BOTTOM) { - /* double the first scanline of the bottom field */ - memcpy (out, field0, row_stride); - out += row_stride; - } - - memcpy (out, field0, row_stride); - out += row_stride; - - for (line = 2; line <= field_height; line++) { - + for (i = 0; i < frame_height; i++) { memset (&scanlines, 0, sizeof (scanlines)); scanlines.bottom_field = (cur_field_flags == PICTURE_INTERLACED_BOTTOM); - /* interp. scanline */ - scanlines.t0 = field0; - scanlines.b0 = field0 + field_stride; + if (!((i & 1) ^ scanlines.bottom_field)) { + /* copying */ + scanlines.tt0 = LINE2 (field0, (i - 2 >= 0) ? i - 2 : i); + scanlines.m0 = LINE2 (field0, i); + scanlines.bb0 = LINE2 (field0, (i + 2 < frame_height ? i + 2 : i)); - if (field1 != NULL) { - scanlines.tt1 = field1; - scanlines.m1 = field1 + field_stride; - scanlines.bb1 = field1 + field_stride * 2; - field1 += field_stride; + scanlines.t1 = LINE2 (field1, i - 1); + scanlines.b1 = LINE2 (field1, i + 1); + + scanlines.tt2 = LINE2 (field2, (i - 2 >= 0) ? i - 2 : i); + scanlines.m2 = LINE2 (field2, i); + scanlines.bb2 = LINE2 (field2, (i + 2 < frame_height ? i + 2 : i)); + + scanlines.t3 = LINE2 (field3, i - 1); + scanlines.b3 = LINE2 (field3, i + 1); + + copy_scanline (self, LINE (dest, i), &scanlines); + } else { + /* interpolating */ + scanlines.t0 = LINE2 (field0, i - 1); + scanlines.b0 = LINE2 (field0, i + 1); + + scanlines.tt1 = LINE2 (field1, (i - 2 >= 0) ? i - 2 : i); + scanlines.m1 = LINE2 (field1, i); + scanlines.bb1 = LINE2 (field1, (i + 2 < frame_height ? i + 2 : i)); + + scanlines.t2 = LINE2 (field2, i - 1); + scanlines.b2 = LINE2 (field2, i + 1); + + scanlines.tt3 = LINE2 (field3, (i - 2 >= 0) ? i - 2 : i); + scanlines.m3 = LINE2 (field3, i); + scanlines.bb3 = LINE2 (field3, (i + 2 < frame_height ? i + 2 : i)); + + interpolate_scanline (self, LINE (dest, i), &scanlines); } - - if (field2 != NULL) { - scanlines.t2 = field2; - scanlines.b2 = field2 + field_stride; - } - - if (field3 != NULL) { - scanlines.tt3 = field3; - scanlines.m3 = field3 + field_stride; - scanlines.bb3 = field3 + field_stride * 2; - field3 += field_stride; - } - - /* set valid data for corner cases */ - if (line == 2) { - scanlines.tt1 = scanlines.bb1; - scanlines.tt3 = scanlines.bb3; - } else if (line == field_height) { - scanlines.bb1 = scanlines.tt1; - scanlines.bb3 = scanlines.tt3; - } - - interpolate_scanline (self, out, &scanlines); - out += row_stride; - - memset (&scanlines, 0, sizeof (scanlines)); - scanlines.bottom_field = (cur_field_flags == PICTURE_INTERLACED_BOTTOM); - - /* copy a scanline */ - scanlines.tt0 = field0; - scanlines.m0 = field0 + field_stride; - scanlines.bb0 = field0 + field_stride * 2; - field0 += field_stride; - - if (field1 != NULL) { - scanlines.t1 = field1; - scanlines.b1 = field1 + field_stride; - } - - if (field2 != NULL) { - scanlines.tt2 = field2; - scanlines.m2 = field2 + field_stride; - scanlines.bb2 = field2 + field_stride * 2; - field2 += field_stride; - } - - if (field3 != NULL) { - scanlines.t3 = field3; - scanlines.b3 = field3 + field_stride; - } - - /* set valid data for corner cases */ - if (line == field_height) { - scanlines.bb0 = scanlines.tt0; - scanlines.b1 = scanlines.t1; - scanlines.bb2 = scanlines.tt2; - scanlines.b3 = scanlines.t3; - } - - copy_scanline (self, out, &scanlines); - out += row_stride; - } - - if (cur_field_flags == PICTURE_INTERLACED_TOP) { - /* double the last scanline of the top field */ - memcpy (out, field0, row_stride); } } @@ -599,10 +515,10 @@ gst_deinterlace_simple_method_deinterlace_frame_planar (GstDeinterlaceMethod * GstDeinterlaceSimpleMethod *self = GST_DEINTERLACE_SIMPLE_METHOD (method); GstDeinterlaceMethodClass *dm_class = GST_DEINTERLACE_METHOD_GET_CLASS (self); guint8 *out; - const guint8 *field0 = NULL, *field1 = NULL, *field2 = NULL, *field3 = NULL; + const guint8 *field0, *field1, *field2, *field3; gint cur_field_idx = history_count - dm_class->fields_required; guint cur_field_flags = history[cur_field_idx].flags; - gint i, row_stride, offset; + gint i, offset; GstDeinterlaceSimpleMethodFunction copy_scanline; GstDeinterlaceSimpleMethodFunction interpolate_scanline; @@ -614,7 +530,6 @@ gst_deinterlace_simple_method_deinterlace_frame_planar (GstDeinterlaceMethod * g_assert (self->copy_scanline_planar[2] != NULL); for (i = 0; i < 3; i++) { - row_stride = self->parent.row_stride[i]; offset = self->parent.offset[i]; copy_scanline = self->copy_scanline_planar[i]; interpolate_scanline = self->interpolate_scanline_planar[i]; @@ -622,27 +537,22 @@ gst_deinterlace_simple_method_deinterlace_frame_planar (GstDeinterlaceMethod * out = GST_BUFFER_DATA (outbuf) + offset; field0 = GST_BUFFER_DATA (history[cur_field_idx].buf) + offset; - if (history[cur_field_idx].flags & PICTURE_INTERLACED_BOTTOM) - field0 += row_stride; g_assert (dm_class->fields_required <= 4); + field1 = NULL; if (dm_class->fields_required >= 2) { field1 = GST_BUFFER_DATA (history[cur_field_idx + 1].buf) + offset; - if (history[cur_field_idx + 1].flags & PICTURE_INTERLACED_BOTTOM) - field1 += row_stride; } + field2 = NULL; if (dm_class->fields_required >= 3) { field2 = GST_BUFFER_DATA (history[cur_field_idx + 2].buf) + offset; - if (history[cur_field_idx + 2].flags & PICTURE_INTERLACED_BOTTOM) - field2 += row_stride; } + field3 = NULL; if (dm_class->fields_required >= 4) { field3 = GST_BUFFER_DATA (history[cur_field_idx + 3].buf) + offset; - if (history[cur_field_idx + 3].flags & PICTURE_INTERLACED_BOTTOM) - field3 += row_stride; } gst_deinterlace_simple_method_deinterlace_frame_planar_plane (self, out, @@ -651,6 +561,52 @@ gst_deinterlace_simple_method_deinterlace_frame_planar (GstDeinterlaceMethod * } } +static void +gst_deinterlace_simple_method_deinterlace_frame_nv12 (GstDeinterlaceMethod * + method, const GstDeinterlaceField * history, guint history_count, + GstBuffer * outbuf) +{ + GstDeinterlaceSimpleMethod *self = GST_DEINTERLACE_SIMPLE_METHOD (method); + GstDeinterlaceMethodClass *dm_class = GST_DEINTERLACE_METHOD_GET_CLASS (self); + guint8 *out; + const guint8 *field0, *field1, *field2, *field3; + gint cur_field_idx = history_count - dm_class->fields_required; + guint cur_field_flags = history[cur_field_idx].flags; + gint i, offset; + + g_assert (self->interpolate_scanline_packed != NULL); + g_assert (self->copy_scanline_packed != NULL); + + for (i = 0; i < 2; i++) { + offset = self->parent.offset[i]; + + out = GST_BUFFER_DATA (outbuf) + offset; + + field0 = GST_BUFFER_DATA (history[cur_field_idx].buf) + offset; + + g_assert (dm_class->fields_required <= 4); + + field1 = NULL; + if (dm_class->fields_required >= 2) { + field1 = GST_BUFFER_DATA (history[cur_field_idx + 1].buf) + offset; + } + + field2 = NULL; + if (dm_class->fields_required >= 3) { + field2 = GST_BUFFER_DATA (history[cur_field_idx + 2].buf) + offset; + } + + field3 = NULL; + if (dm_class->fields_required >= 4) { + field3 = GST_BUFFER_DATA (history[cur_field_idx + 3].buf) + offset; + } + + gst_deinterlace_simple_method_deinterlace_frame_planar_plane (self, out, + field0, field1, field2, field3, cur_field_flags, i, + self->copy_scanline_packed, self->interpolate_scanline_packed); + } +} + static void gst_deinterlace_simple_method_setup (GstDeinterlaceMethod * method, GstVideoFormat format, gint width, gint height) @@ -721,6 +677,14 @@ gst_deinterlace_simple_method_setup (GstDeinterlaceMethod * method, self->interpolate_scanline_packed = klass->interpolate_scanline_bgr; self->copy_scanline_packed = klass->copy_scanline_bgr; break; + case GST_VIDEO_FORMAT_NV12: + self->interpolate_scanline_packed = klass->interpolate_scanline_nv12; + self->copy_scanline_packed = klass->copy_scanline_nv12; + break; + case GST_VIDEO_FORMAT_NV21: + self->interpolate_scanline_packed = klass->interpolate_scanline_nv21; + self->copy_scanline_packed = klass->copy_scanline_nv21; + break; case GST_VIDEO_FORMAT_I420: case GST_VIDEO_FORMAT_YV12: case GST_VIDEO_FORMAT_Y444: @@ -777,6 +741,10 @@ gst_deinterlace_simple_method_class_init (GstDeinterlaceSimpleMethodClass gst_deinterlace_simple_method_deinterlace_frame_planar; dm_class->deinterlace_frame_y41b = gst_deinterlace_simple_method_deinterlace_frame_planar; + dm_class->deinterlace_frame_nv12 = + gst_deinterlace_simple_method_deinterlace_frame_nv12; + dm_class->deinterlace_frame_nv21 = + gst_deinterlace_simple_method_deinterlace_frame_nv12; dm_class->fields_required = 2; dm_class->setup = gst_deinterlace_simple_method_setup; dm_class->supported = gst_deinterlace_simple_method_supported; @@ -797,6 +765,10 @@ gst_deinterlace_simple_method_class_init (GstDeinterlaceSimpleMethodClass gst_deinterlace_simple_method_interpolate_scanline_packed; klass->copy_scanline_uyvy = gst_deinterlace_simple_method_copy_scanline_packed; + klass->interpolate_scanline_nv12 = + gst_deinterlace_simple_method_interpolate_scanline_packed; + klass->copy_scanline_nv12 = + gst_deinterlace_simple_method_copy_scanline_packed; klass->interpolate_scanline_argb = gst_deinterlace_simple_method_interpolate_scanline_packed; @@ -815,7 +787,6 @@ gst_deinterlace_simple_method_class_init (GstDeinterlaceSimpleMethodClass gst_deinterlace_simple_method_interpolate_scanline_packed; klass->copy_scanline_bgra = gst_deinterlace_simple_method_copy_scanline_packed; - klass->interpolate_scanline_rgb = gst_deinterlace_simple_method_interpolate_scanline_packed; klass->copy_scanline_rgb = gst_deinterlace_simple_method_copy_scanline_packed; diff --git a/gst/deinterlace/gstdeinterlacemethod.h b/gst/deinterlace/gstdeinterlacemethod.h index d8d3343619..11e2c35ac1 100644 --- a/gst/deinterlace/gstdeinterlacemethod.h +++ b/gst/deinterlace/gstdeinterlacemethod.h @@ -95,6 +95,8 @@ struct _GstDeinterlaceMethodClass { GstDeinterlaceMethodDeinterlaceFunction deinterlace_frame_y42b; GstDeinterlaceMethodDeinterlaceFunction deinterlace_frame_y41b; GstDeinterlaceMethodDeinterlaceFunction deinterlace_frame_ayuv; + GstDeinterlaceMethodDeinterlaceFunction deinterlace_frame_nv12; + GstDeinterlaceMethodDeinterlaceFunction deinterlace_frame_nv21; GstDeinterlaceMethodDeinterlaceFunction deinterlace_frame_argb; GstDeinterlaceMethodDeinterlaceFunction deinterlace_frame_abgr; GstDeinterlaceMethodDeinterlaceFunction deinterlace_frame_rgba; @@ -199,6 +201,12 @@ struct _GstDeinterlaceSimpleMethodClass { GstDeinterlaceSimpleMethodFunction interpolate_scanline_bgr; GstDeinterlaceSimpleMethodFunction copy_scanline_bgr; + /* Semi-planar formats */ + GstDeinterlaceSimpleMethodFunction interpolate_scanline_nv12; + GstDeinterlaceSimpleMethodFunction copy_scanline_nv12; + GstDeinterlaceSimpleMethodFunction interpolate_scanline_nv21; + GstDeinterlaceSimpleMethodFunction copy_scanline_nv21; + /* Planar formats */ GstDeinterlaceSimpleMethodFunction copy_scanline_planar_y; GstDeinterlaceSimpleMethodFunction interpolate_scanline_planar_y; diff --git a/gst/deinterlace/tvtime/greedy.c b/gst/deinterlace/tvtime/greedy.c index 383bc3409c..dcb495b7ee 100644 --- a/gst/deinterlace/tvtime/greedy.c +++ b/gst/deinterlace/tvtime/greedy.c @@ -49,20 +49,12 @@ GType gst_deinterlace_method_greedy_l_get_type (void); typedef struct { - GstDeinterlaceMethod parent; + GstDeinterlaceSimpleMethod parent; guint max_comb; } GstDeinterlaceMethodGreedyL; -typedef void (*GreedyLScanlineFunction) (GstDeinterlaceMethodGreedyL * self, - const guint8 * L2, const guint8 * L1, const guint8 * L3, const guint8 * L2P, - guint8 * Dest, gint width); - -typedef struct -{ - GstDeinterlaceMethodClass parent_class; - GreedyLScanlineFunction scanline; -} GstDeinterlaceMethodGreedyLClass; +typedef GstDeinterlaceSimpleMethodClass GstDeinterlaceMethodGreedyLClass; // This is a simple lightweight DeInterlace method that uses little CPU time // but gives very good results for low or intermedite motion. @@ -77,186 +69,61 @@ typedef struct // Blended Clip but this give too good results for the CPU to ignore here. static inline void -deinterlace_greedy_scanline_orc (GstDeinterlaceMethodGreedyL * self, - const guint8 * m0, const guint8 * t1, - const guint8 * b1, const guint8 * m2, guint8 * output, gint width) +deinterlace_greedy_interpolate_scanline_orc (GstDeinterlaceSimpleMethod * self, + guint8 * out, const GstDeinterlaceScanlineData * scanlines) { - deinterlace_line_greedy (output, m0, t1, b1, m2, self->max_comb, width); + /* FIXME - is this safe or just a hack? */ + guint max_comb = GST_DEINTERLACE_METHOD_GREEDY_L (self)->max_comb; + + deinterlace_line_greedy (out, scanlines->m3, scanlines->t2, scanlines->b2, + scanlines->m1, max_comb, self->parent.row_stride[0]); +} + +static inline void +deinterlace_greedy_interpolate_scanline_orc_planar_u (GstDeinterlaceSimpleMethod + * self, guint8 * out, const GstDeinterlaceScanlineData * scanlines) +{ + /* FIXME - is this safe or just a hack? */ + guint max_comb = GST_DEINTERLACE_METHOD_GREEDY_L (self)->max_comb; + + deinterlace_line_greedy (out, scanlines->m3, scanlines->t2, scanlines->b2, + scanlines->m1, max_comb, self->parent.row_stride[1]); +} + +static inline void +deinterlace_greedy_interpolate_scanline_orc_planar_v (GstDeinterlaceSimpleMethod + * self, guint8 * out, const GstDeinterlaceScanlineData * scanlines) +{ + /* FIXME - is this safe or just a hack? */ + guint max_comb = GST_DEINTERLACE_METHOD_GREEDY_L (self)->max_comb; + + deinterlace_line_greedy (out, scanlines->m3, scanlines->t2, scanlines->b2, + scanlines->m1, max_comb, self->parent.row_stride[2]); } static void -deinterlace_frame_di_greedy_packed (GstDeinterlaceMethod * method, - const GstDeinterlaceField * history, guint history_count, - GstBuffer * outbuf) +deinterlace_greedy_copy_scanline (GstDeinterlaceSimpleMethod * self, + guint8 * out, const GstDeinterlaceScanlineData * scanlines) { - GstDeinterlaceMethodGreedyL *self = GST_DEINTERLACE_METHOD_GREEDY_L (method); - GstDeinterlaceMethodGreedyLClass *klass = - GST_DEINTERLACE_METHOD_GREEDY_L_GET_CLASS (self); - gint InfoIsOdd = 0; - gint Line; - gint RowStride = method->row_stride[0]; - gint FieldHeight = method->frame_height / 2; - gint Pitch = method->row_stride[0] * 2; - const guint8 *L1; // ptr to Line1, of 3 - const guint8 *L2; // ptr to Line2, the weave line - const guint8 *L3; // ptr to Line3 - const guint8 *L2P; // ptr to prev Line2 - guint8 *Dest = GST_BUFFER_DATA (outbuf); - - // copy first even line no matter what, and the first odd line if we're - // processing an EVEN field. (note diff from other deint rtns.) - - if (history[history_count - 1].flags == PICTURE_INTERLACED_BOTTOM) { - InfoIsOdd = 1; - - L1 = GST_BUFFER_DATA (history[history_count - 2].buf); - if (history[history_count - 2].flags & PICTURE_INTERLACED_BOTTOM) - L1 += RowStride; - - L2 = GST_BUFFER_DATA (history[history_count - 1].buf); - if (history[history_count - 1].flags & PICTURE_INTERLACED_BOTTOM) - L2 += RowStride; - - L3 = L1 + Pitch; - L2P = GST_BUFFER_DATA (history[history_count - 3].buf); - if (history[history_count - 3].flags & PICTURE_INTERLACED_BOTTOM) - L2P += RowStride; - - // copy first even line - memcpy (Dest, L1, RowStride); - Dest += RowStride; - } else { - InfoIsOdd = 0; - L1 = GST_BUFFER_DATA (history[history_count - 2].buf); - if (history[history_count - 2].flags & PICTURE_INTERLACED_BOTTOM) - L1 += RowStride; - - L2 = GST_BUFFER_DATA (history[history_count - 1].buf) + Pitch; - if (history[history_count - 1].flags & PICTURE_INTERLACED_BOTTOM) - L2 += RowStride; - - L3 = L1 + Pitch; - L2P = GST_BUFFER_DATA (history[history_count - 3].buf) + Pitch; - if (history[history_count - 3].flags & PICTURE_INTERLACED_BOTTOM) - L2P += RowStride; - - // copy first even line - memcpy (Dest, L1, RowStride); - Dest += RowStride; - // then first odd line - memcpy (Dest, L1, RowStride); - Dest += RowStride; - } - - for (Line = 0; Line < (FieldHeight - 1); ++Line) { - klass->scanline (self, L2, L1, L3, L2P, Dest, RowStride); - Dest += RowStride; - memcpy (Dest, L3, RowStride); - Dest += RowStride; - - L1 += Pitch; - L2 += Pitch; - L3 += Pitch; - L2P += Pitch; - } - - if (InfoIsOdd) { - memcpy (Dest, L2, RowStride); - } + memcpy (out, scanlines->m2, self->parent.row_stride[0]); } static void -deinterlace_frame_di_greedy_planar_plane (GstDeinterlaceMethodGreedyL * self, - const guint8 * L1, const guint8 * L2, const guint8 * L3, const guint8 * L2P, - guint8 * Dest, gint RowStride, gint FieldHeight, gint Pitch, gint InfoIsOdd, - GreedyLScanlineFunction scanline) +deinterlace_greedy_copy_scanline_planar_u (GstDeinterlaceSimpleMethod * self, + guint8 * out, const GstDeinterlaceScanlineData * scanlines) { - gint Line; - - // copy first even line no matter what, and the first odd line if we're - // processing an EVEN field. (note diff from other deint rtns.) - - if (InfoIsOdd) { - // copy first even line - memcpy (Dest, L1, RowStride); - Dest += RowStride; - } else { - // copy first even line - memcpy (Dest, L1, RowStride); - Dest += RowStride; - // then first odd line - memcpy (Dest, L1, RowStride); - Dest += RowStride; - } - - for (Line = 0; Line < (FieldHeight - 1); ++Line) { - scanline (self, L2, L1, L3, L2P, Dest, RowStride); - Dest += RowStride; - memcpy (Dest, L3, RowStride); - Dest += RowStride; - - L1 += Pitch; - L2 += Pitch; - L3 += Pitch; - L2P += Pitch; - } - - if (InfoIsOdd) { - memcpy (Dest, L2, RowStride); - } + memcpy (out, scanlines->m2, self->parent.row_stride[1]); } static void -deinterlace_frame_di_greedy_planar (GstDeinterlaceMethod * method, - const GstDeinterlaceField * history, guint history_count, - GstBuffer * outbuf) +deinterlace_greedy_copy_scanline_planar_v (GstDeinterlaceSimpleMethod * self, + guint8 * out, const GstDeinterlaceScanlineData * scanlines) { - GstDeinterlaceMethodGreedyL *self = GST_DEINTERLACE_METHOD_GREEDY_L (method); - GstDeinterlaceMethodGreedyLClass *klass = - GST_DEINTERLACE_METHOD_GREEDY_L_GET_CLASS (self); - gint InfoIsOdd; - gint RowStride; - gint FieldHeight; - gint Pitch; - const guint8 *L1; // ptr to Line1, of 3 - const guint8 *L2; // ptr to Line2, the weave line - const guint8 *L3; // ptr to Line3 - const guint8 *L2P; // ptr to prev Line2 - guint8 *Dest; - gint i; - gint Offset; - GreedyLScanlineFunction scanline = klass->scanline; - - for (i = 0; i < 3; i++) { - Offset = method->offset[i]; - - InfoIsOdd = (history[history_count - 1].flags == PICTURE_INTERLACED_BOTTOM); - RowStride = method->row_stride[i]; - FieldHeight = method->height[i] / 2; - Pitch = method->row_stride[i] * 2; - - Dest = GST_BUFFER_DATA (outbuf) + Offset; - - L1 = GST_BUFFER_DATA (history[history_count - 2].buf) + Offset; - if (history[history_count - 2].flags & PICTURE_INTERLACED_BOTTOM) - L1 += RowStride; - - L2 = GST_BUFFER_DATA (history[history_count - 1].buf) + Offset; - if (history[history_count - 1].flags & PICTURE_INTERLACED_BOTTOM) - L2 += RowStride; - - L3 = L1 + Pitch; - L2P = GST_BUFFER_DATA (history[history_count - 3].buf) + Offset; - if (history[history_count - 3].flags & PICTURE_INTERLACED_BOTTOM) - L2P += RowStride; - - deinterlace_frame_di_greedy_planar_plane (self, L1, L2, L3, L2P, Dest, - RowStride, FieldHeight, Pitch, InfoIsOdd, scanline); - } + memcpy (out, scanlines->m2, self->parent.row_stride[2]); } G_DEFINE_TYPE (GstDeinterlaceMethodGreedyL, gst_deinterlace_method_greedy_l, - GST_TYPE_DEINTERLACE_METHOD); + GST_TYPE_DEINTERLACE_SIMPLE_METHOD); enum { @@ -299,6 +166,8 @@ gst_deinterlace_method_greedy_l_class_init (GstDeinterlaceMethodGreedyLClass * klass) { GstDeinterlaceMethodClass *dim_class = (GstDeinterlaceMethodClass *) klass; + GstDeinterlaceSimpleMethodClass *dism_class = + (GstDeinterlaceSimpleMethodClass *) klass; GObjectClass *gobject_class = (GObjectClass *) klass; gobject_class->set_property = gst_deinterlace_method_greedy_l_set_property; @@ -315,23 +184,48 @@ gst_deinterlace_method_greedy_l_class_init (GstDeinterlaceMethodGreedyLClass * dim_class->nick = "greedyl"; dim_class->latency = 1; - dim_class->deinterlace_frame_yuy2 = deinterlace_frame_di_greedy_packed; - dim_class->deinterlace_frame_yvyu = deinterlace_frame_di_greedy_packed; - dim_class->deinterlace_frame_uyvy = deinterlace_frame_di_greedy_packed; - dim_class->deinterlace_frame_y444 = deinterlace_frame_di_greedy_planar; - dim_class->deinterlace_frame_y42b = deinterlace_frame_di_greedy_planar; - dim_class->deinterlace_frame_i420 = deinterlace_frame_di_greedy_planar; - dim_class->deinterlace_frame_yv12 = deinterlace_frame_di_greedy_planar; - dim_class->deinterlace_frame_y41b = deinterlace_frame_di_greedy_planar; - dim_class->deinterlace_frame_ayuv = deinterlace_frame_di_greedy_planar; - dim_class->deinterlace_frame_argb = deinterlace_frame_di_greedy_packed; - dim_class->deinterlace_frame_rgba = deinterlace_frame_di_greedy_packed; - dim_class->deinterlace_frame_abgr = deinterlace_frame_di_greedy_packed; - dim_class->deinterlace_frame_bgra = deinterlace_frame_di_greedy_packed; - dim_class->deinterlace_frame_rgb = deinterlace_frame_di_greedy_packed; - dim_class->deinterlace_frame_bgr = deinterlace_frame_di_greedy_packed; + dism_class->interpolate_scanline_ayuv = + deinterlace_greedy_interpolate_scanline_orc; + dism_class->interpolate_scanline_yuy2 = + deinterlace_greedy_interpolate_scanline_orc; + dism_class->interpolate_scanline_yvyu = + deinterlace_greedy_interpolate_scanline_orc; + dism_class->interpolate_scanline_uyvy = + deinterlace_greedy_interpolate_scanline_orc; + dism_class->interpolate_scanline_argb = + deinterlace_greedy_interpolate_scanline_orc; + dism_class->interpolate_scanline_abgr = + deinterlace_greedy_interpolate_scanline_orc; + dism_class->interpolate_scanline_rgba = + deinterlace_greedy_interpolate_scanline_orc; + dism_class->interpolate_scanline_bgra = + deinterlace_greedy_interpolate_scanline_orc; + dism_class->interpolate_scanline_rgb = + deinterlace_greedy_interpolate_scanline_orc; + dism_class->interpolate_scanline_bgr = + deinterlace_greedy_interpolate_scanline_orc; + dism_class->interpolate_scanline_planar_y = + deinterlace_greedy_interpolate_scanline_orc; + dism_class->interpolate_scanline_planar_u = + deinterlace_greedy_interpolate_scanline_orc_planar_u; + dism_class->interpolate_scanline_planar_v = + deinterlace_greedy_interpolate_scanline_orc_planar_v; - klass->scanline = deinterlace_greedy_scanline_orc; + dism_class->copy_scanline_ayuv = deinterlace_greedy_copy_scanline; + dism_class->copy_scanline_yuy2 = deinterlace_greedy_copy_scanline; + dism_class->copy_scanline_yvyu = deinterlace_greedy_copy_scanline; + dism_class->copy_scanline_uyvy = deinterlace_greedy_copy_scanline; + dism_class->copy_scanline_argb = deinterlace_greedy_copy_scanline; + dism_class->copy_scanline_abgr = deinterlace_greedy_copy_scanline; + dism_class->copy_scanline_rgba = deinterlace_greedy_copy_scanline; + dism_class->copy_scanline_bgra = deinterlace_greedy_copy_scanline; + dism_class->copy_scanline_rgb = deinterlace_greedy_copy_scanline; + dism_class->copy_scanline_bgr = deinterlace_greedy_copy_scanline; + dism_class->copy_scanline_planar_y = deinterlace_greedy_copy_scanline; + dism_class->copy_scanline_planar_u = + deinterlace_greedy_copy_scanline_planar_u; + dism_class->copy_scanline_planar_v = + deinterlace_greedy_copy_scanline_planar_v; } static void diff --git a/gst/deinterlace/tvtime/linear.c b/gst/deinterlace/tvtime/linear.c index 97bb7e517f..05dac451de 100644 --- a/gst/deinterlace/tvtime/linear.c +++ b/gst/deinterlace/tvtime/linear.c @@ -112,6 +112,8 @@ gst_deinterlace_method_linear_class_init (GstDeinterlaceMethodLinearClass * dism_class->interpolate_scanline_bgra = deinterlace_scanline_linear_packed_c; dism_class->interpolate_scanline_rgb = deinterlace_scanline_linear_packed_c; dism_class->interpolate_scanline_bgr = deinterlace_scanline_linear_packed_c; + dism_class->interpolate_scanline_nv12 = deinterlace_scanline_linear_packed_c; + dism_class->interpolate_scanline_nv21 = deinterlace_scanline_linear_packed_c; dism_class->interpolate_scanline_planar_y = deinterlace_scanline_linear_planar_y_c; dism_class->interpolate_scanline_planar_u = diff --git a/gst/deinterlace/tvtime/linearblend.c b/gst/deinterlace/tvtime/linearblend.c index 548ce7c9f0..a343d0fde2 100644 --- a/gst/deinterlace/tvtime/linearblend.c +++ b/gst/deinterlace/tvtime/linearblend.c @@ -167,6 +167,10 @@ static void deinterlace_scanline_linear_blend_packed_c; dism_class->interpolate_scanline_bgr = deinterlace_scanline_linear_blend_packed_c; + dism_class->interpolate_scanline_nv12 = + deinterlace_scanline_linear_blend_packed_c; + dism_class->interpolate_scanline_nv21 = + deinterlace_scanline_linear_blend_packed_c; dism_class->interpolate_scanline_planar_y = deinterlace_scanline_linear_blend_planar_y_c; diff --git a/gst/deinterlace/tvtime/scalerbob.c b/gst/deinterlace/tvtime/scalerbob.c index 4612ef05dc..bac4c62b71 100644 --- a/gst/deinterlace/tvtime/scalerbob.c +++ b/gst/deinterlace/tvtime/scalerbob.c @@ -90,6 +90,10 @@ gst_deinterlace_method_scaler_bob_class_init (GstDeinterlaceMethodScalerBobClass deinterlace_scanline_scaler_bob_packed; dism_class->interpolate_scanline_uyvy = deinterlace_scanline_scaler_bob_packed; + dism_class->interpolate_scanline_nv12 = + deinterlace_scanline_scaler_bob_packed; + dism_class->interpolate_scanline_nv21 = + deinterlace_scanline_scaler_bob_packed; dism_class->interpolate_scanline_argb = deinterlace_scanline_scaler_bob_packed; dism_class->interpolate_scanline_abgr = diff --git a/gst/deinterlace/tvtime/vfir.c b/gst/deinterlace/tvtime/vfir.c index da32bf69fb..c589dd7444 100644 --- a/gst/deinterlace/tvtime/vfir.c +++ b/gst/deinterlace/tvtime/vfir.c @@ -259,6 +259,8 @@ gst_deinterlace_method_vfir_class_init (GstDeinterlaceMethodVFIRClass * klass) dism_class->interpolate_scanline_yuy2 = deinterlace_line_packed_mmx; dism_class->interpolate_scanline_yvyu = deinterlace_line_packed_mmx; dism_class->interpolate_scanline_uyvy = deinterlace_line_packed_mmx; + dism_class->interpolate_scanline_nv12 = deinterlace_line_packed_mmx; + dism_class->interpolate_scanline_nv21 = deinterlace_line_packed_mmx; dism_class->interpolate_scanline_argb = deinterlace_line_packed_mmx; dism_class->interpolate_scanline_abgr = deinterlace_line_packed_mmx; dism_class->interpolate_scanline_rgba = deinterlace_line_packed_mmx; @@ -273,6 +275,8 @@ gst_deinterlace_method_vfir_class_init (GstDeinterlaceMethodVFIRClass * klass) dism_class->interpolate_scanline_yvyu = deinterlace_line_packed_c; dism_class->interpolate_scanline_uyvy = deinterlace_line_packed_c; dism_class->interpolate_scanline_ayuv = deinterlace_line_packed_c; + dism_class->interpolate_scanline_nv12 = deinterlace_line_packed_c; + dism_class->interpolate_scanline_nv21 = deinterlace_line_packed_c; dism_class->interpolate_scanline_argb = deinterlace_line_packed_c; dism_class->interpolate_scanline_abgr = deinterlace_line_packed_c; dism_class->interpolate_scanline_rgba = deinterlace_line_packed_c; @@ -288,6 +292,8 @@ gst_deinterlace_method_vfir_class_init (GstDeinterlaceMethodVFIRClass * klass) dism_class->interpolate_scanline_yuy2 = deinterlace_line_packed_c; dism_class->interpolate_scanline_yvyu = deinterlace_line_packed_c; dism_class->interpolate_scanline_uyvy = deinterlace_line_packed_c; + dism_class->interpolate_scanline_nv12 = deinterlace_line_packed_c; + dism_class->interpolate_scanline_nv21 = deinterlace_line_packed_c; dism_class->interpolate_scanline_argb = deinterlace_line_packed_c; dism_class->interpolate_scanline_abgr = deinterlace_line_packed_c; dism_class->interpolate_scanline_rgba = deinterlace_line_packed_c; diff --git a/gst/deinterlace/tvtime/weave.c b/gst/deinterlace/tvtime/weave.c index 0f9714235a..8d49979da2 100644 --- a/gst/deinterlace/tvtime/weave.c +++ b/gst/deinterlace/tvtime/weave.c @@ -119,6 +119,8 @@ gst_deinterlace_method_weave_class_init (GstDeinterlaceMethodWeaveClass * klass) dism_class->interpolate_scanline_yuy2 = deinterlace_scanline_weave_packed; dism_class->interpolate_scanline_yvyu = deinterlace_scanline_weave_packed; dism_class->interpolate_scanline_uyvy = deinterlace_scanline_weave_packed; + dism_class->interpolate_scanline_nv12 = deinterlace_scanline_weave_packed; + dism_class->interpolate_scanline_nv21 = deinterlace_scanline_weave_packed; dism_class->interpolate_scanline_argb = deinterlace_scanline_weave_packed; dism_class->interpolate_scanline_abgr = deinterlace_scanline_weave_packed; dism_class->interpolate_scanline_rgba = deinterlace_scanline_weave_packed; @@ -136,6 +138,8 @@ gst_deinterlace_method_weave_class_init (GstDeinterlaceMethodWeaveClass * klass) dism_class->copy_scanline_yuy2 = copy_scanline_packed; dism_class->copy_scanline_yvyu = copy_scanline_packed; dism_class->copy_scanline_uyvy = copy_scanline_packed; + dism_class->copy_scanline_nv12 = copy_scanline_packed; + dism_class->copy_scanline_nv21 = copy_scanline_packed; dism_class->copy_scanline_argb = copy_scanline_packed; dism_class->copy_scanline_abgr = copy_scanline_packed; dism_class->copy_scanline_rgba = copy_scanline_packed; diff --git a/gst/deinterlace/tvtime/weavebff.c b/gst/deinterlace/tvtime/weavebff.c index 9c95ba816a..b96da1f417 100644 --- a/gst/deinterlace/tvtime/weavebff.c +++ b/gst/deinterlace/tvtime/weavebff.c @@ -140,6 +140,8 @@ gst_deinterlace_method_weave_bff_class_init (GstDeinterlaceMethodWeaveBFFClass * dism_class->interpolate_scanline_yuy2 = deinterlace_scanline_weave_packed; dism_class->interpolate_scanline_yvyu = deinterlace_scanline_weave_packed; dism_class->interpolate_scanline_uyvy = deinterlace_scanline_weave_packed; + dism_class->interpolate_scanline_nv12 = deinterlace_scanline_weave_packed; + dism_class->interpolate_scanline_nv21 = deinterlace_scanline_weave_packed; dism_class->interpolate_scanline_argb = deinterlace_scanline_weave_packed; dism_class->interpolate_scanline_abgr = deinterlace_scanline_weave_packed; dism_class->interpolate_scanline_rgba = deinterlace_scanline_weave_packed; @@ -157,6 +159,8 @@ gst_deinterlace_method_weave_bff_class_init (GstDeinterlaceMethodWeaveBFFClass * dism_class->copy_scanline_yuy2 = copy_scanline_packed; dism_class->copy_scanline_yvyu = copy_scanline_packed; dism_class->copy_scanline_uyvy = copy_scanline_packed; + dism_class->copy_scanline_nv12 = copy_scanline_packed; + dism_class->copy_scanline_nv21 = copy_scanline_packed; dism_class->copy_scanline_argb = copy_scanline_packed; dism_class->copy_scanline_abgr = copy_scanline_packed; dism_class->copy_scanline_rgba = copy_scanline_packed; diff --git a/gst/deinterlace/tvtime/weavetff.c b/gst/deinterlace/tvtime/weavetff.c index 2df0863856..b645b0b905 100644 --- a/gst/deinterlace/tvtime/weavetff.c +++ b/gst/deinterlace/tvtime/weavetff.c @@ -141,6 +141,8 @@ gst_deinterlace_method_weave_tff_class_init (GstDeinterlaceMethodWeaveTFFClass * dism_class->interpolate_scanline_yuy2 = deinterlace_scanline_weave_packed; dism_class->interpolate_scanline_yvyu = deinterlace_scanline_weave_packed; dism_class->interpolate_scanline_uyvy = deinterlace_scanline_weave_packed; + dism_class->interpolate_scanline_nv12 = deinterlace_scanline_weave_packed; + dism_class->interpolate_scanline_nv21 = deinterlace_scanline_weave_packed; dism_class->interpolate_scanline_argb = deinterlace_scanline_weave_packed; dism_class->interpolate_scanline_abgr = deinterlace_scanline_weave_packed; dism_class->interpolate_scanline_rgba = deinterlace_scanline_weave_packed; @@ -158,6 +160,8 @@ gst_deinterlace_method_weave_tff_class_init (GstDeinterlaceMethodWeaveTFFClass * dism_class->copy_scanline_yuy2 = copy_scanline_packed; dism_class->copy_scanline_yvyu = copy_scanline_packed; dism_class->copy_scanline_uyvy = copy_scanline_packed; + dism_class->copy_scanline_nv12 = copy_scanline_packed; + dism_class->copy_scanline_nv21 = copy_scanline_packed; dism_class->copy_scanline_argb = copy_scanline_packed; dism_class->copy_scanline_abgr = copy_scanline_packed; dism_class->copy_scanline_rgba = copy_scanline_packed; diff --git a/gst/effectv/gstop.c b/gst/effectv/gstop.c index d9e35dfe52..de798dc89b 100644 --- a/gst/effectv/gstop.c +++ b/gst/effectv/gstop.c @@ -161,14 +161,14 @@ setOpmap (gint8 * opmap[4], gint width, gint height) #endif opmap[OP_SPIRAL1][i] = ((guint) - ((at / M_PI * 256) + (r * 4000))) & 255; + ((at / G_PI * 256) + (r * 4000))) & 255; j = r * 300 / 32; rr = r * 300 - j * 32; j *= 64; j += (rr > 28) ? (rr - 28) * 16 : 0; opmap[OP_SPIRAL2][i] = ((guint) - ((at / M_PI * 4096) + (r * 1600) - j)) & 255; + ((at / G_PI * 4096) + (r * 1600) - j)) & 255; opmap[OP_PARABOLA][i] = ((guint) (yy / (xx * xx * 0.3 + 0.1) * 400)) & 255; diff --git a/gst/effectv/gstradioac.c b/gst/effectv/gstradioac.c index 85b4ecc400..58c1b7b9e8 100644 --- a/gst/effectv/gstradioac.c +++ b/gst/effectv/gstradioac.c @@ -287,7 +287,7 @@ blurzoomcore (GstRadioacTV * filter) /* Background image is refreshed every frame */ static void -image_bgsubtract_update_y (guint32 * src, guint32 * background, guint8 * diff, +image_bgsubtract_update_y (guint32 * src, gint16 * background, guint8 * diff, gint video_area, gint y_threshold) { gint i; @@ -298,7 +298,7 @@ image_bgsubtract_update_y (guint32 * src, guint32 * background, guint8 * diff, gint v; p = src; - q = (gint16 *) background; + q = background; r = diff; for (i = 0; i < video_area; i++) { R = ((*p) & 0xff0000) >> (16 - 1); @@ -446,7 +446,7 @@ gst_radioactv_set_caps (GstBaseTransform * btrans, GstCaps * incaps, if (filter->background) g_free (filter->background); - filter->background = g_new (guint32, filter->width * filter->height); + filter->background = g_new0 (gint16, filter->width * filter->height); setTable (filter); diff --git a/gst/effectv/gstradioac.h b/gst/effectv/gstradioac.h index c80fa0a01f..34ad8ed721 100644 --- a/gst/effectv/gstradioac.h +++ b/gst/effectv/gstradioac.h @@ -64,7 +64,7 @@ struct _GstRadioacTV guint32 *snapframe; guint8 *blurzoombuf; guint8 *diff; - guint32 *background; + gint16 *background; gint *blurzoomx; gint *blurzoomy; diff --git a/gst/equalizer/gstiirequalizer.c b/gst/equalizer/gstiirequalizer.c index b679d4d9d7..0b767489ea 100644 --- a/gst/equalizer/gstiirequalizer.c +++ b/gst/equalizer/gstiirequalizer.c @@ -441,11 +441,11 @@ calculate_omega (gdouble freq, gint rate) gdouble omega; if (freq / rate >= 0.5) - omega = M_PI; + omega = G_PI; else if (freq <= 0.0) omega = 0.0; else - omega = 2.0 * M_PI * (freq / rate); + omega = 2.0 * G_PI * (freq / rate); return omega; } @@ -456,10 +456,10 @@ calculate_bw (GstIirEqualizerBand * band, gint rate) gdouble bw = 0.0; if (band->width / rate >= 0.5) { - /* If bandwidth == 0.5 the calculation below fails as tan(M_PI/2) + /* If bandwidth == 0.5 the calculation below fails as tan(G_PI/2) * is undefined. So set the bandwidth to a slightly smaller value. */ - bw = M_PI - 0.00000001; + bw = G_PI - 0.00000001; } else if (band->width <= 0.0) { /* If bandwidth == 0 this band won't change anything so set * the coefficients accordingly. The coefficient calculation @@ -472,7 +472,7 @@ calculate_bw (GstIirEqualizerBand * band, gint rate) band->b1 = 0.0; band->b2 = 0.0; } else { - bw = 2.0 * M_PI * (band->width / rate); + bw = 2.0 * G_PI * (band->width / rate); } return bw; } diff --git a/gst/flv/Makefile.am b/gst/flv/Makefile.am index b2d37ebbb5..ab08cdd9e7 100644 --- a/gst/flv/Makefile.am +++ b/gst/flv/Makefile.am @@ -1,9 +1,9 @@ plugin_LTLIBRARIES = libgstflv.la libgstflv_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) -libgstflv_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(GST_LIBS) \ - -lgstpbutils-@GST_MAJORMINOR@ -libgstflv_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS} +libgstflv_la_LIBADD = -lgstpbutils-@GST_MAJORMINOR@ \ + $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(GST_LIBS) +libgstflv_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS} libgstflv_la_SOURCES = gstflvdemux.c gstflvmux.c libgstflv_la_LIBTOOLFLAGS = --tag=disable-static diff --git a/gst/flv/gstflvdemux.c b/gst/flv/gstflvdemux.c index 04d4b5b639..87ebf0e869 100644 --- a/gst/flv/gstflvdemux.c +++ b/gst/flv/gstflvdemux.c @@ -40,6 +40,7 @@ #include #include #include +#include static GstStaticPadTemplate flv_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -128,8 +129,9 @@ gst_flv_demux_parse_and_add_index_entry (GstFlvDemux * demux, GstClockTime ts, associations[1].value = pos; gst_index_add_associationv (demux->index, demux->index_id, - (keyframe) ? GST_ASSOCIATION_FLAG_KEY_UNIT : GST_ASSOCIATION_FLAG_NONE, - 2, (const GstIndexAssociation *) &associations); + (keyframe) ? GST_ASSOCIATION_FLAG_KEY_UNIT : + GST_ASSOCIATION_FLAG_DELTA_UNIT, 2, + (const GstIndexAssociation *) &associations); if (pos > demux->index_max_pos) demux->index_max_pos = pos; @@ -517,7 +519,7 @@ gst_flv_demux_parse_tag_script (GstFlvDemux * demux, GstBuffer * buffer) return GST_FLOW_OK; } - /* The number of elements is just a hint, some files have + /* The number of elements is just a hint, some files have nb_elements == 0 and actually contain items. */ GST_DEBUG_OBJECT (demux, "there are approx. %d elements in the array", nb_elems); @@ -575,6 +577,7 @@ gst_flv_demux_audio_negotiate (GstFlvDemux * demux, guint32 codec_tag, GstCaps *caps = NULL; gchar *codec_name = NULL; gboolean ret = FALSE; + guint adjusted_rate = rate; switch (codec_tag) { case 1: @@ -603,9 +606,29 @@ gst_flv_demux_audio_negotiate (GstFlvDemux * demux, guint32 codec_tag, caps = gst_caps_new_simple ("audio/x-nellymoser", NULL); break; case 10: + { + /* use codec-data to extract and verify samplerate */ + if (demux->audio_codec_data && + GST_BUFFER_SIZE (demux->audio_codec_data) >= 2) { + gint freq_index; + + freq_index = + ((GST_READ_UINT16_BE (GST_BUFFER_DATA (demux->audio_codec_data)))); + freq_index = (freq_index & 0x0780) >> 7; + adjusted_rate = + gst_codec_utils_aac_get_sample_rate_from_index (freq_index); + + if (adjusted_rate && (rate != adjusted_rate)) { + GST_LOG_OBJECT (demux, "Ajusting AAC sample rate %d -> %d", rate, + adjusted_rate); + } else { + adjusted_rate = rate; + } + } caps = gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE, NULL); break; + } case 7: caps = gst_caps_new_simple ("audio/x-alaw", NULL); break; @@ -624,8 +647,8 @@ gst_flv_demux_audio_negotiate (GstFlvDemux * demux, guint32 codec_tag, goto beach; } - gst_caps_set_simple (caps, - "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL); + gst_caps_set_simple (caps, "rate", G_TYPE_INT, adjusted_rate, + "channels", G_TYPE_INT, channels, NULL); if (demux->audio_codec_data) { gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, @@ -635,7 +658,7 @@ gst_flv_demux_audio_negotiate (GstFlvDemux * demux, guint32 codec_tag, ret = gst_pad_set_caps (demux->audio_pad, caps); if (G_LIKELY (ret)) { - /* Store the caps we have set */ + /* Store the caps we got from tags */ demux->audio_codec_tag = codec_tag; demux->rate = rate; demux->channels = channels; @@ -807,7 +830,7 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer) gst_object_ref (demux->audio_pad)); /* We only emit no more pads when we have audio and video. Indeed we can - * not trust the FLV header to tell us if there will be only audio or + * not trust the FLV header to tell us if there will be only audio or * only video and we would just break discovery of some files */ if (demux->audio_pad && demux->video_pad) { GST_DEBUG_OBJECT (demux, "emitting no more pads"); @@ -851,7 +874,7 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer) switch (aac_packet_type) { case 0: { - /* AudioSpecificConfic data */ + /* AudioSpecificConfig data */ GST_LOG_OBJECT (demux, "got an AAC codec data packet"); if (demux->audio_codec_data) { gst_buffer_unref (demux->audio_codec_data); @@ -1125,7 +1148,7 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer) goto beach; } - /* When we ve set pixel-aspect-ratio we use that boolean to detect a + /* When we ve set pixel-aspect-ratio we use that boolean to detect a * metadata tag that would come later and trigger a caps change */ demux->got_par = FALSE; @@ -1150,7 +1173,7 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer) gst_object_ref (demux->video_pad)); /* We only emit no more pads when we have audio and video. Indeed we can - * not trust the FLV header to tell us if there will be only audio or + * not trust the FLV header to tell us if there will be only audio or * only video and we would just break discovery of some files */ if (demux->audio_pad && demux->video_pad) { GST_DEBUG_OBJECT (demux, "emitting no more pads"); @@ -1170,7 +1193,7 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer) goto beach; } - /* When we ve set pixel-aspect-ratio we use that boolean to detect a + /* When we ve set pixel-aspect-ratio we use that boolean to detect a * metadata tag that would come later and trigger a caps change */ demux->got_par = FALSE; } diff --git a/gst/flv/gstflvmux.c b/gst/flv/gstflvmux.c index e731604711..68a0df4fc7 100644 --- a/gst/flv/gstflvmux.c +++ b/gst/flv/gstflvmux.c @@ -685,9 +685,11 @@ gst_flv_mux_create_metadata (GstFlvMux * mux) /* Some players expect the 'duration' to be always set. Fill it out later, after querying the pads or after getting EOS */ - tmp = gst_flv_mux_create_number_script_value ("duration", 0); - script_tag = gst_buffer_join (script_tag, tmp); - tags_written++; + if (!mux->streamable) { + tmp = gst_flv_mux_create_number_script_value ("duration", 86400); + script_tag = gst_buffer_join (script_tag, tmp); + tags_written++; + } /* Sometimes the information about the total file size is useful for the player. It will be filled later, after getting EOS */ diff --git a/gst/goom/convolve_fx.c b/gst/goom/convolve_fx.c index 349de4d28e..457b5eb2cd 100644 --- a/gst/goom/convolve_fx.c +++ b/gst/goom/convolve_fx.c @@ -76,7 +76,7 @@ compute_tables (VisualFX * _this, PluginInfo * info) data->h_height = info->screen.height; for (i = 0; i < NB_THETA; i++) { - radian = 2 * i * M_PI / NB_THETA; + radian = 2 * i * G_PI / NB_THETA; h = (0.2 + cos (radian) / 15.0 * sin (radian * 2.0 + 12.123)) * screen_coef; data->h_cos[i] = 0x10000 * (-h * cos (radian) * cos (radian)); data->h_sin[i] = 0x10000 * (h * sin (radian + 1.57) * sin (radian)); diff --git a/gst/goom/ifs.c b/gst/goom/ifs.c index 9c59d927fb..3684dcc516 100644 --- a/gst/goom/ifs.c +++ b/gst/goom/ifs.c @@ -177,8 +177,8 @@ Random_Simis (PluginInfo * goomInfo, FRACTAL * F, SIMI * Cur, int i) Cur->c_y = Gauss_Rand (goomInfo, 0.0, .8, 4.0); Cur->r = Gauss_Rand (goomInfo, F->r_mean, F->dr_mean, 3.0); Cur->r2 = Half_Gauss_Rand (goomInfo, 0.0, F->dr2_mean, 2.0); - Cur->A = Gauss_Rand (goomInfo, 0.0, 360.0, 4.0) * (M_PI / 180.0); - Cur->A2 = Gauss_Rand (goomInfo, 0.0, 360.0, 4.0) * (M_PI / 180.0); + Cur->A = Gauss_Rand (goomInfo, 0.0, 360.0, 4.0) * (G_PI / 180.0); + Cur->A2 = Gauss_Rand (goomInfo, 0.0, 360.0, 4.0) * (G_PI / 180.0); Cur++; } } diff --git a/gst/goom/lines.c b/gst/goom/lines.c index 849fbe6e8f..096240cda9 100644 --- a/gst/goom/lines.c +++ b/gst/goom/lines.c @@ -70,7 +70,7 @@ genline (int id, float param, GMUnitPointer * l, int rx, int ry) for (i = 0; i < 512; i++) { l[i].x = ((float) i * rx) / 512.0f; l[i].y = param; - l[i].angle = M_PI / 2.0f; + l[i].angle = G_PI / 2.0f; } return; case GML_VLINE: @@ -84,7 +84,7 @@ genline (int id, float param, GMUnitPointer * l, int rx, int ry) for (i = 0; i < 512; i++) { float cosa, sina; - l[i].angle = 2.0f * M_PI * (float) i / 512.0f; + l[i].angle = 2.0f * G_PI * (float) i / 512.0f; cosa = param * cos (l[i].angle); sina = param * sin (l[i].angle); l[i].x = ((float) rx / 2.0f) + cosa; diff --git a/gst/goom/tentacle3d.c b/gst/goom/tentacle3d.c index 83de43e6b7..4c0d1438d1 100644 --- a/gst/goom/tentacle3d.c +++ b/gst/goom/tentacle3d.c @@ -51,7 +51,7 @@ typedef struct _TENTACLE_FX_DATA /* statics from pretty_move */ float distt; float distt2; - float rot; /* entre 0 et 2 * M_PI */ + float rot; /* entre 0 et 2 * G_PI */ int happens; int rotation; int lock; @@ -85,7 +85,7 @@ tentacle_fx_init (VisualFX * _this, PluginInfo * info) data->distt = 10.0f; data->distt2 = 0.0f; - data->rot = 0.0f; /* entre 0 et 2 * M_PI */ + data->rot = 0.0f; /* entre 0 et 2 * G_PI */ data->happens = 0; data->rotation = 0; @@ -253,27 +253,27 @@ pretty_move (PluginInfo * goomInfo, float cycle, float *dist, float *dist2, *dist = fx_data->distt = (tmp + 3.0f * fx_data->distt) / 4.0f; if (!fx_data->happens) { - tmp = M_PI * sin (cycle) / 32 + 3 * M_PI / 2; + tmp = G_PI * sin (cycle) / 32 + 3 * G_PI / 2; } else { fx_data->rotation = goom_irand (goomInfo->gRandom, 500) ? fx_data->rotation : goom_irand (goomInfo->gRandom, 2); if (fx_data->rotation) - cycle *= 2.0f * M_PI; + cycle *= 2.0f * G_PI; else - cycle *= -1.0f * M_PI; - tmp = cycle - (M_PI * 2.0) * floor (cycle / (M_PI * 2.0)); + cycle *= -1.0f * G_PI; + tmp = cycle - (G_PI * 2.0) * floor (cycle / (G_PI * 2.0)); } - if (abs (tmp - fx_data->rot) > abs (tmp - (fx_data->rot + 2.0 * M_PI))) { - fx_data->rot = (tmp + 15.0f * (fx_data->rot + 2 * M_PI)) / 16.0f; - if (fx_data->rot > 2.0 * M_PI) - fx_data->rot -= 2.0 * M_PI; + if (abs (tmp - fx_data->rot) > abs (tmp - (fx_data->rot + 2.0 * G_PI))) { + fx_data->rot = (tmp + 15.0f * (fx_data->rot + 2 * G_PI)) / 16.0f; + if (fx_data->rot > 2.0 * G_PI) + fx_data->rot -= 2.0 * G_PI; *rotangle = fx_data->rot; - } else if (abs (tmp - fx_data->rot) > abs (tmp - (fx_data->rot - 2.0 * M_PI))) { - fx_data->rot = (tmp + 15.0f * (fx_data->rot - 2.0 * M_PI)) / 16.0f; + } else if (abs (tmp - fx_data->rot) > abs (tmp - (fx_data->rot - 2.0 * G_PI))) { + fx_data->rot = (tmp + 15.0f * (fx_data->rot - 2.0 * G_PI)) / 16.0f; if (fx_data->rot < 0.0f) - fx_data->rot += 2.0 * M_PI; + fx_data->rot += 2.0 * G_PI; *rotangle = fx_data->rot; } else *rotangle = fx_data->rot = (tmp + 15.0f * fx_data->rot) / 16.0f; diff --git a/gst/icydemux/gsticydemux.c b/gst/icydemux/gsticydemux.c index 5965500560..25829598fa 100644 --- a/gst/icydemux/gsticydemux.c +++ b/gst/icydemux/gsticydemux.c @@ -301,6 +301,7 @@ gst_icydemux_unicodify (const gchar * str) return gst_tag_freeform_string_to_utf8 (str, -1, env_vars); } +/* takes ownership of tag list */ static gboolean gst_icydemux_tag_found (GstICYDemux * icydemux, GstTagList * tags) { @@ -309,10 +310,13 @@ gst_icydemux_tag_found (GstICYDemux * icydemux, GstTagList * tags) return gst_icydemux_send_tag_event (icydemux, tags); /* if we haven't a source pad yet, cache the tags */ - if (!icydemux->cached_tags) - icydemux->cached_tags = gst_tag_list_new (); - - gst_tag_list_insert (icydemux->cached_tags, tags, GST_TAG_MERGE_REPLACE_ALL); + if (!icydemux->cached_tags) { + icydemux->cached_tags = tags; + } else { + gst_tag_list_insert (icydemux->cached_tags, tags, + GST_TAG_MERGE_REPLACE_ALL); + gst_tag_list_free (tags); + } return TRUE; } @@ -320,12 +324,11 @@ gst_icydemux_tag_found (GstICYDemux * icydemux, GstTagList * tags) static void gst_icydemux_parse_and_send_tags (GstICYDemux * icydemux) { - GstTagList *tags = gst_tag_list_new (); + GstTagList *tags; const guint8 *data; int length, i; gchar *buffer; gchar **strings; - gboolean found_tag = FALSE; length = gst_adapter_available (icydemux->meta_adapter); @@ -333,10 +336,9 @@ gst_icydemux_parse_and_send_tags (GstICYDemux * icydemux) /* Now, copy this to a buffer where we can NULL-terminate it to make things * a bit easier, then do that parsing. */ - buffer = g_malloc (length + 1); - memcpy (buffer, data, length); - buffer[length] = 0; + buffer = g_strndup ((const gchar *) data, length); + tags = gst_tag_list_new (); strings = g_strsplit (buffer, "';", 0); for (i = 0; strings[i]; i++) { @@ -347,7 +349,6 @@ gst_icydemux_parse_and_send_tags (GstICYDemux * icydemux) gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, title, NULL); g_free (title); - found_tag = TRUE; } } else if (!g_ascii_strncasecmp (strings[i], "StreamUrl=", 10)) { char *url = gst_icydemux_unicodify (strings[i] + 11); @@ -356,7 +357,6 @@ gst_icydemux_parse_and_send_tags (GstICYDemux * icydemux) gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_HOMEPAGE, url, NULL); g_free (url); - found_tag = TRUE; } } } @@ -365,8 +365,10 @@ gst_icydemux_parse_and_send_tags (GstICYDemux * icydemux) g_free (buffer); gst_adapter_clear (icydemux->meta_adapter); - if (found_tag) + if (!gst_tag_list_is_empty (tags)) gst_icydemux_tag_found (icydemux, tags); + else + gst_tag_list_free (tags); } static gboolean @@ -379,7 +381,7 @@ gst_icydemux_handle_event (GstPad * pad, GstEvent * event) GstTagList *tags; gst_event_parse_tag (event, &tags); - result = gst_icydemux_tag_found (icydemux, tags); + result = gst_icydemux_tag_found (icydemux, gst_tag_list_copy (tags)); gst_event_unref (event); return result; } @@ -617,6 +619,7 @@ gst_icydemux_change_state (GstElement * element, GstStateChange transition) return ret; } +/* takes ownership of tag list */ static gboolean gst_icydemux_send_tag_event (GstICYDemux * icydemux, GstTagList * tags) { diff --git a/gst/id3demux/id3tags.c b/gst/id3demux/id3tags.c index 709cda87d3..d201d7c289 100644 --- a/gst/id3demux/id3tags.c +++ b/gst/id3demux/id3tags.c @@ -159,7 +159,7 @@ id3demux_read_id3v2_tag (GstBuffer * buffer, guint * id3v2_size, GST_WARNING ("ID3v2 tag is from revision 2.%d.%d, " "but decoder only supports 2.%d.%d. Ignoring as per spec.", version >> 8, version & 0xff, ID3V2_VERSION >> 8, ID3V2_VERSION & 0xff); - return ID3TAGS_READ_TAG; + return ID3TAGS_BROKEN_TAG; } GST_DEBUG ("ID3v2 header flags: %s %s %s %s", diff --git a/gst/id3demux/id3v2frames.c b/gst/id3demux/id3v2frames.c index 021a3d173c..e51bbb78ec 100644 --- a/gst/id3demux/id3v2frames.c +++ b/gst/id3demux/id3v2frames.c @@ -875,7 +875,7 @@ id3v2_genre_fields_to_taglist (ID3TagsWorking * work, const gchar * tag_name, for (i = 0; i < tag_fields->len; i++) { gint len; - tag_str = g_array_index (tag_fields, gchar *, 0); + tag_str = g_array_index (tag_fields, gchar *, i); if (tag_str == NULL) continue; diff --git a/gst/matroska/Makefile.am b/gst/matroska/Makefile.am index 96777bd06a..5efbf4eb03 100644 --- a/gst/matroska/Makefile.am +++ b/gst/matroska/Makefile.am @@ -5,6 +5,7 @@ libgstmatroska_la_SOURCES = \ ebml-write.c \ matroska.c \ matroska-demux.c \ + matroska-parse.c \ matroska-ids.c \ matroska-mux.c \ webm-mux.c \ @@ -15,6 +16,7 @@ noinst_HEADERS = \ ebml-read.h \ ebml-write.h \ matroska-demux.h \ + matroska-parse.h \ matroska-ids.h \ matroska-mux.h \ webm-mux.h \ diff --git a/gst/matroska/ebml-read.c b/gst/matroska/ebml-read.c index 379c882d08..24780e7775 100644 --- a/gst/matroska/ebml-read.c +++ b/gst/matroska/ebml-read.c @@ -262,7 +262,8 @@ gst_ebml_read_master (GstEbmlRead * ebml, guint32 * id) return ret; /* we just at least peeked the id */ - g_assert (gst_byte_reader_skip (gst_ebml_read_br (ebml), prefix)); + if (!gst_byte_reader_skip (gst_ebml_read_br (ebml), prefix)) + return GST_FLOW_ERROR; /* FIXME: do proper error handling */ m.offset = gst_ebml_read_get_pos (ebml); if (!gst_byte_reader_get_data (gst_ebml_read_br (ebml), length, &data)) @@ -329,7 +330,8 @@ gst_ebml_read_buffer (GstEbmlRead * ebml, guint32 * id, GstBuffer ** buf) return ret; /* we just at least peeked the id */ - g_assert (gst_byte_reader_skip (gst_ebml_read_br (ebml), prefix)); + if (!gst_byte_reader_skip (gst_ebml_read_br (ebml), prefix)) + return GST_FLOW_ERROR; /* FIXME: do proper error handling */ if (G_LIKELY (length > 0)) { guint offset; @@ -367,7 +369,8 @@ gst_ebml_read_bytes (GstEbmlRead * ebml, guint32 * id, const guint8 ** data, return ret; /* we just at least peeked the id */ - g_assert (gst_byte_reader_skip (gst_ebml_read_br (ebml), prefix)); + if (!gst_byte_reader_skip (gst_ebml_read_br (ebml), prefix)) + return GST_FLOW_ERROR; /* FIXME: do proper error handling */ *data = NULL; if (G_LIKELY (length >= 0)) { diff --git a/gst/matroska/ebml-write.c b/gst/matroska/ebml-write.c index ed39c51e16..b66098709d 100644 --- a/gst/matroska/ebml-write.c +++ b/gst/matroska/ebml-write.c @@ -133,6 +133,12 @@ gst_ebml_write_reset (GstEbmlWrite * ebml) gst_byte_writer_free (ebml->cache); ebml->cache = NULL; } + + if (ebml->caps) { + gst_caps_unref (ebml->caps); + ebml->caps = NULL; + } + ebml->last_write_result = GST_FLOW_OK; ebml->timestamp = GST_CLOCK_TIME_NONE; } diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 3bc8cd4f8d..401554b435 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -165,11 +165,11 @@ static GstIndex *gst_matroska_demux_get_index (GstElement * element); /* caps functions */ static GstCaps *gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext - * videocontext, - const gchar * codec_id, guint8 * data, guint size, gchar ** codec_name); + * videocontext, const gchar * codec_id, guint8 * data, guint size, + gchar ** codec_name, guint32 * riff_fourcc); static GstCaps *gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext - * audiocontext, - const gchar * codec_id, guint8 * data, guint size, gchar ** codec_name); + * audiocontext, const gchar * codec_id, guint8 * data, guint size, + gchar ** codec_name, guint16 * riff_audio_fmt); static GstCaps * gst_matroska_demux_subtitle_caps (GstMatroskaTrackSubtitleContext * subtitlecontext, const gchar * codec_id, gpointer data, guint size); @@ -229,10 +229,6 @@ gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass) GObjectClass *gobject_class = (GObjectClass *) klass; GstElementClass *gstelement_class = (GstElementClass *) klass; - /* parser helper separate debug */ - GST_DEBUG_CATEGORY_INIT (ebmlread_debug, "ebmlread", - 0, "EBML stream helper class"); - GST_DEBUG_CATEGORY_INIT (matroskademux_debug, "matroskademux", 0, "Matroska demuxer"); @@ -399,6 +395,11 @@ gst_matroska_demux_reset (GstElement * element) demux->index = NULL; } + if (demux->clusters) { + g_array_free (demux->clusters, TRUE); + demux->clusters = NULL; + } + /* reset timers */ demux->clock = NULL; demux->time_scale = 1000000; @@ -1173,7 +1174,8 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) GstCaps *caps = NULL; gchar *padname = NULL; GstFlowReturn ret; - guint32 id; + guint32 id, riff_fourcc = 0; + guint16 riff_audio_fmt = 0; GstTagList *list = NULL; gchar *codec = NULL; @@ -1852,8 +1854,9 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) padname = g_strdup_printf ("video_%02d", demux->num_v_streams++); templ = gst_element_class_get_pad_template (klass, "video_%02d"); caps = gst_matroska_demux_video_caps (videocontext, - context->codec_id, - (guint8 *) context->codec_priv, context->codec_priv_size, &codec); + context->codec_id, (guint8 *) context->codec_priv, + context->codec_priv_size, &codec, &riff_fourcc); + if (codec) { list = gst_tag_list_new (); gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, @@ -1870,8 +1873,9 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) padname = g_strdup_printf ("audio_%02d", demux->num_a_streams++); templ = gst_element_class_get_pad_template (klass, "audio_%02d"); caps = gst_matroska_demux_audio_caps (audiocontext, - context->codec_id, - context->codec_priv, context->codec_priv_size, &codec); + context->codec_id, context->codec_priv, context->codec_priv_size, + &codec, &riff_audio_fmt); + if (codec) { list = gst_tag_list_new (); gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, @@ -1940,6 +1944,12 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) } gst_caps_set_simple (caps, "codec-id", G_TYPE_STRING, context->codec_id, NULL); + + /* add any unrecognised riff fourcc / audio format, but after codec-id */ + if (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO && riff_audio_fmt != 0) + gst_caps_set_simple (caps, "format", G_TYPE_INT, riff_audio_fmt, NULL); + else if (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO && riff_fourcc != 0) + gst_caps_set_simple (caps, "fourcc", GST_TYPE_FOURCC, riff_fourcc, NULL); } /* the pad in here */ @@ -2062,7 +2072,7 @@ gst_matroska_demux_query (GstMatroskaDemux * demux, GstPad * pad, /* assuming we'll be able to get an index ... */ seekable = demux->seekable; } else { - seekable = ! !demux->index; + seekable = TRUE; } gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, @@ -2317,6 +2327,17 @@ gst_matroska_demux_move_to_entry (GstMatroskaDemux * demux, return TRUE; } +static gint +gst_matroska_cluster_compare (gint64 * i1, gint64 * i2) +{ + if (*i1 < *i2) + return -1; + else if (*i1 > *i2) + return 1; + else + return 0; +} + /* searches for a cluster start from @pos, * return GST_FLOW_OK and cluster position in @pos if found */ static GstFlowReturn @@ -2333,6 +2354,30 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos) orig_offset = demux->offset; + GST_LOG_OBJECT (demux, "searching cluster following offset %" G_GINT64_FORMAT, + *pos); + + if (demux->clusters) { + gint64 *cpos; + + cpos = gst_util_array_binary_search (demux->clusters->data, + demux->clusters->len, sizeof (gint64), + (GCompareDataFunc) gst_matroska_cluster_compare, + GST_SEARCH_MODE_AFTER, pos, NULL); + /* sanity check */ + if (cpos) { + GST_DEBUG_OBJECT (demux, + "cluster reported at offset %" G_GINT64_FORMAT, *cpos); + demux->offset = *cpos; + ret = + gst_matroska_demux_peek_id_length_pull (demux, &id, &length, &needed); + if (ret == GST_FLOW_OK && id == GST_MATROSKA_ID_CLUSTER) { + newpos = *cpos; + goto exit; + } + } + } + /* read in at newpos and scan for ebml cluster id */ while (1) { GstByteReader reader; @@ -2344,13 +2389,13 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos) GST_DEBUG_OBJECT (demux, "read buffer size %d at offset %" G_GINT64_FORMAT, GST_BUFFER_SIZE (buf), newpos); gst_byte_reader_init_from_buffer (&reader, buf); - cluster_pos = 0; resume: cluster_pos = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff, - GST_MATROSKA_ID_CLUSTER, cluster_pos, - GST_BUFFER_SIZE (buf) - cluster_pos); + GST_MATROSKA_ID_CLUSTER, 0, gst_byte_reader_get_remaining (&reader)); if (cluster_pos >= 0) { newpos += cluster_pos; + /* prepare resuming at next byte */ + gst_byte_reader_skip (&reader, cluster_pos + 1); GST_DEBUG_OBJECT (demux, "found cluster ebml id at offset %" G_GINT64_FORMAT, newpos); /* extra checks whether we really sync'ed to a cluster: @@ -2390,7 +2435,7 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos) goto resume; } else { /* partial cluster id may have been in tail of buffer */ - newpos += MAX (GST_BUFFER_SIZE (buf), 4) - 3; + newpos += MAX (gst_byte_reader_get_remaining (&reader), 4) - 3; gst_buffer_unref (buf); buf = NULL; } @@ -2401,6 +2446,7 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos) buf = NULL; } +exit: demux->offset = orig_offset; *pos = newpos; return ret; @@ -2643,8 +2689,8 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux, entry->pos + demux->ebml_segment_start); } - flush = ! !(flags & GST_SEEK_FLAG_FLUSH); - keyunit = ! !(flags & GST_SEEK_FLAG_KEY_UNIT); + flush = !!(flags & GST_SEEK_FLAG_FLUSH); + keyunit = !!(flags & GST_SEEK_FLAG_KEY_UNIT); if (flush) { GST_DEBUG_OBJECT (demux, "Starting flush"); @@ -3090,26 +3136,30 @@ gst_matroska_demux_parse_header (GstMatroskaDemux * demux, GstEbmlRead * ebml) } exit: - ret = GST_FLOW_ERROR; - if (doctype) { - if (g_str_equal (doctype, GST_MATROSKA_DOCTYPE_MATROSKA) || - g_str_equal (doctype, GST_MATROSKA_DOCTYPE_WEBM)) { - if (version <= 2) { + + if ((doctype != NULL && !strcmp (doctype, GST_MATROSKA_DOCTYPE_MATROSKA)) || + (doctype != NULL && !strcmp (doctype, GST_MATROSKA_DOCTYPE_WEBM)) || + (doctype == NULL)) { + if (version <= 2) { + if (doctype) { GST_INFO_OBJECT (demux, "Input is %s version %d", doctype, version); - ret = GST_FLOW_OK; } else { - GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), - ("Demuxer version (2) is too old to read %s version %d", - doctype, version)); + GST_WARNING_OBJECT (demux, "Input is EBML without doctype, assuming " + "matroska (version %d)", version); } + ret = GST_FLOW_OK; } else { - GST_ELEMENT_ERROR (demux, STREAM, WRONG_TYPE, (NULL), - ("Input is not a matroska stream (doctype=%s)", doctype)); + GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), + ("Demuxer version (2) is too old to read %s version %d", + GST_STR_NULL (doctype), version)); + ret = GST_FLOW_ERROR; } g_free (doctype); } else { GST_ELEMENT_ERROR (demux, STREAM, WRONG_TYPE, (NULL), - ("Input is not a matroska stream")); + ("Input is not a matroska stream (doctype=%s)", doctype)); + ret = GST_FLOW_ERROR; + g_free (doctype); } return ret; @@ -4148,17 +4198,10 @@ gst_matroska_demux_push_hdr_buf (GstMatroskaDemux * demux, GstMatroskaTrackContext * stream, guint8 * data, guint len) { GstFlowReturn ret, cret; - GstBuffer *header_buf = NULL; - - ret = gst_pad_alloc_buffer_and_set_caps (stream->pad, - GST_BUFFER_OFFSET_NONE, len, stream->caps, &header_buf); - - /* we combine but don't use the combined value to check if we have a buffer - * or not. The combined value is what we return. */ - cret = gst_matroska_demux_combine_flows (demux, stream, ret); - if (ret != GST_FLOW_OK) - goto no_buffer; + GstBuffer *header_buf; + header_buf = gst_buffer_new_and_alloc (len); + gst_buffer_set_caps (header_buf, stream->caps); memcpy (GST_BUFFER_DATA (header_buf), data, len); if (stream->set_discont) { @@ -4172,14 +4215,6 @@ gst_matroska_demux_push_hdr_buf (GstMatroskaDemux * demux, cret = gst_matroska_demux_combine_flows (demux, stream, ret); return cret; - - /* ERRORS */ -no_buffer: - { - GST_DEBUG_OBJECT (demux, "could not alloc buffer: %s, combined %s", - gst_flow_get_name (ret), gst_flow_get_name (cret)); - return cret; - } } static GstFlowReturn @@ -4388,7 +4423,6 @@ static GstFlowReturn gst_matroska_demux_add_mpeg_seq_header (GstElement * element, GstMatroskaTrackContext * stream, GstBuffer ** buf) { - GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element); guint8 *seq_header; guint seq_header_len; guint32 header; @@ -4414,20 +4448,11 @@ gst_matroska_demux_add_mpeg_seq_header (GstElement * element, /* Sequence start code, if not found prepend */ if (header != 0x000001b3) { GstBuffer *newbuf; - GstFlowReturn ret, cret; - ret = gst_pad_alloc_buffer_and_set_caps (stream->pad, - GST_BUFFER_OFFSET_NONE, GST_BUFFER_SIZE (*buf) + seq_header_len, - stream->caps, &newbuf); - cret = gst_matroska_demux_combine_flows (demux, stream, ret); - if (ret != GST_FLOW_OK) { - GST_WARNING_OBJECT (demux, "Reallocating buffer for sequence header " - "failed: %s, combined flow return: %s", gst_flow_get_name (ret), - gst_flow_get_name (cret)); - return cret; - } + newbuf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (*buf) + seq_header_len); + gst_buffer_set_caps (newbuf, stream->caps); - GST_DEBUG_OBJECT (demux, "Prepending MPEG sequence header"); + GST_DEBUG_OBJECT (element, "Prepending MPEG sequence header"); gst_buffer_copy_metadata (newbuf, *buf, GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_FLAGS); g_memmove (GST_BUFFER_DATA (newbuf), seq_header, seq_header_len); @@ -4444,13 +4469,11 @@ static GstFlowReturn gst_matroska_demux_add_wvpk_header (GstElement * element, GstMatroskaTrackContext * stream, GstBuffer ** buf) { - GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element); GstMatroskaTrackAudioContext *audiocontext = (GstMatroskaTrackAudioContext *) stream; GstBuffer *newbuf = NULL; guint8 *data; guint newlen; - GstFlowReturn ret, cret = GST_FLOW_OK; Wavpack4Header wvh; wvh.ck_id[0] = 'w'; @@ -4477,15 +4500,8 @@ gst_matroska_demux_add_wvpk_header (GstElement * element, /* block_samples, flags and crc are already in the buffer */ newlen = GST_BUFFER_SIZE (*buf) + sizeof (Wavpack4Header) - 12; - ret = - gst_pad_alloc_buffer_and_set_caps (stream->pad, GST_BUFFER_OFFSET_NONE, - newlen, stream->caps, &newbuf); - cret = gst_matroska_demux_combine_flows (demux, stream, ret); - if (ret != GST_FLOW_OK) { - GST_DEBUG_OBJECT (demux, "pad_alloc failed %s, combined %s", - gst_flow_get_name (ret), gst_flow_get_name (cret)); - return cret; - } + newbuf = gst_buffer_new_and_alloc (newlen); + gst_buffer_set_caps (newbuf, stream->caps); data = GST_BUFFER_DATA (newbuf); data[0] = 'w'; @@ -4514,7 +4530,7 @@ gst_matroska_demux_add_wvpk_header (GstElement * element, size = GST_BUFFER_SIZE (*buf); if (size < 4) { - GST_ERROR_OBJECT (demux, "Too small wavpack buffer"); + GST_ERROR_OBJECT (element, "Too small wavpack buffer"); return GST_FLOW_ERROR; } @@ -4581,7 +4597,7 @@ gst_matroska_demux_add_wvpk_header (GstElement * element, audiocontext->wvpk_block_index += block_samples; } - return cret; + return GST_FLOW_OK; } static GstFlowReturn @@ -5404,6 +5420,17 @@ gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux, break; } + case GST_MATROSKA_ID_CLUSTER: + { + guint64 pos = seek_pos + demux->ebml_segment_start; + + GST_LOG_OBJECT (demux, "Cluster position"); + if (G_UNLIKELY (!demux->clusters)) + demux->clusters = g_array_sized_new (TRUE, TRUE, sizeof (guint64), 100); + g_array_append_val (demux->clusters, pos); + break; + } + default: GST_DEBUG_OBJECT (demux, "Ignoring Seek entry for ID=0x%x", seek_id); break; @@ -5450,15 +5477,21 @@ gst_matroska_demux_parse_contents (GstMatroskaDemux * demux, GstEbmlRead * ebml) DEBUG_ELEMENT_STOP (demux, ebml, "SeekHead", ret); + /* Sort clusters by position for easier searching */ + if (demux->clusters) + g_array_sort (demux->clusters, (GCompareFunc) gst_matroska_cluster_compare); + return ret; } #define GST_FLOW_OVERFLOW GST_FLOW_CUSTOM_ERROR +#define MAX_BLOCK_SIZE (15 * 1024 * 1024) + static inline GstFlowReturn gst_matroska_demux_check_read_size (GstMatroskaDemux * demux, guint64 bytes) { - if (G_UNLIKELY (bytes > 10 * 1024 * 1024)) { + if (G_UNLIKELY (bytes > MAX_BLOCK_SIZE)) { /* only a few blocks are expected/allowed to be large, * and will be recursed into, whereas others will be read and must fit */ if (demux->streaming) { @@ -6286,7 +6319,7 @@ gst_matroska_demux_sink_activate_pull (GstPad * sinkpad, gboolean active) static GstCaps * gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * videocontext, const gchar * codec_id, guint8 * data, guint size, - gchar ** codec_name) + gchar ** codec_name, guint32 * riff_fourcc) { GstMatroskaTrackContext *context = (GstMatroskaTrackContext *) videocontext; GstCaps *caps = NULL; @@ -6298,6 +6331,9 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * context->send_flac_headers = FALSE; context->send_speex_headers = FALSE; + if (riff_fourcc) + *riff_fourcc = 0; + /* TODO: check if we have all codec types from matroska-ids.h * check if we have to do more special things with codec_private * @@ -6344,9 +6380,17 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * GST_BUFFER_SIZE (buf)); } + if (riff_fourcc) + *riff_fourcc = vids->compression; + caps = gst_riff_create_video_caps (vids->compression, NULL, vids, buf, NULL, codec_name); + if (caps == NULL) { + GST_WARNING ("Unhandled RIFF fourcc %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (vids->compression)); + } + if (buf) gst_buffer_unref (buf); @@ -6456,6 +6500,8 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, priv, NULL); gst_buffer_unref (priv); + gst_caps_set_simple (caps, "stream-format", G_TYPE_STRING, "avc", + "alignment", G_TYPE_STRING, "au", NULL); } *codec_name = g_strdup ("H264"); } else if ((!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1)) || @@ -6652,7 +6698,7 @@ aac_profile_idx (const gchar * codec_id) static GstCaps * gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * audiocontext, const gchar * codec_id, guint8 * data, guint size, - gchar ** codec_name) + gchar ** codec_name, guint16 * riff_audio_fmt) { GstMatroskaTrackContext *context = (GstMatroskaTrackContext *) audiocontext; GstCaps *caps = NULL; @@ -6660,6 +6706,9 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * g_assert (audiocontext != NULL); g_assert (codec_name != NULL); + if (riff_audio_fmt) + *riff_audio_fmt = 0; + context->send_xiph_headers = FALSE; context->send_flac_headers = FALSE; context->send_speex_headers = FALSE; @@ -6753,9 +6802,16 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * /* 18 is the waveformatex size */ gst_buffer_set_data (codec_data, data + 18, auds.size); + if (riff_audio_fmt) + *riff_audio_fmt = auds.format; + caps = gst_riff_create_audio_caps (auds.format, NULL, &auds, NULL, codec_data, codec_name); gst_buffer_unref (codec_data); + + if (caps == NULL) { + GST_WARNING ("Unhandled RIFF audio format 0x%02x", auds.format); + } } } else if (g_str_has_prefix (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_AAC)) { GstBuffer *priv = NULL; @@ -7048,6 +7104,10 @@ gst_matroska_demux_plugin_init (GstPlugin * plugin) { gst_riff_init (); + /* parser helper separate debug */ + GST_DEBUG_CATEGORY_INIT (ebmlread_debug, "ebmlread", + 0, "EBML stream helper class"); + /* create an elementfactory for the matroska_demux element */ if (!gst_element_register (plugin, "matroskademux", GST_RANK_PRIMARY, GST_TYPE_MATROSKA_DEMUX)) diff --git a/gst/matroska/matroska-demux.h b/gst/matroska/matroska-demux.h index b31f79cb6a..a35a5935ac 100644 --- a/gst/matroska/matroska-demux.h +++ b/gst/matroska/matroska-demux.h @@ -92,6 +92,8 @@ typedef struct _GstMatroskaDemux { /* a cue (index) table */ GArray *index; + /* cluster positions (optional) */ + GArray *clusters; /* timescale in the file */ guint64 time_scale; diff --git a/gst/matroska/matroska-mux.c b/gst/matroska/matroska-mux.c index fea9c356fe..31fe40bfc3 100644 --- a/gst/matroska/matroska-mux.c +++ b/gst/matroska/matroska-mux.c @@ -46,6 +46,7 @@ #endif #include +#include #include #include @@ -89,7 +90,7 @@ static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src", "width = (int) [ 16, 4096 ], " \ "height = (int) [ 16, 4096 ] " -/* FIXME: +/* FIXME: * * require codec data, etc as needed */ @@ -203,7 +204,7 @@ static GstStaticPadTemplate subtitlesink_templ = GST_STATIC_PAD_TEMPLATE ("subtitle_%d", GST_PAD_SINK, GST_PAD_REQUEST, - GST_STATIC_CAPS_ANY); + GST_STATIC_CAPS ("subtitle/x-kate")); static GArray *used_uids; G_LOCK_DEFINE_STATIC (used_uids); @@ -571,7 +572,7 @@ gst_matroska_mux_reset (GstElement * element) * @pad: Pad which received the event. * @event: Received event. * - * handle events - copied from oggmux without understanding + * handle events - copied from oggmux without understanding * * Returns: #TRUE on success. */ @@ -1790,32 +1791,55 @@ gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps) */ static GstPad * gst_matroska_mux_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * pad_name) + GstPadTemplate * templ, const gchar * req_name) { GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); GstMatroskaMux *mux = GST_MATROSKA_MUX (element); GstMatroskaPad *collect_pad; GstPad *newpad = NULL; gchar *name = NULL; + const gchar *pad_name = NULL; GstPadSetCapsFunction setcapsfunc = NULL; GstMatroskaTrackContext *context = NULL; + gint pad_id; if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) { - name = g_strdup_printf ("audio_%d", mux->num_a_streams++); + /* don't mix named and unnamed pads, if the pad already exists we fail when + * trying to add it */ + if (req_name != NULL && sscanf (req_name, "audio_%d", &pad_id) == 1) { + pad_name = req_name; + } else { + name = g_strdup_printf ("audio_%d", mux->num_a_streams++); + pad_name = name; + } setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps); context = (GstMatroskaTrackContext *) g_new0 (GstMatroskaTrackAudioContext, 1); context->type = GST_MATROSKA_TRACK_TYPE_AUDIO; context->name = g_strdup ("Audio"); } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) { - name = g_strdup_printf ("video_%d", mux->num_v_streams++); + /* don't mix named and unnamed pads, if the pad already exists we fail when + * trying to add it */ + if (req_name != NULL && sscanf (req_name, "video_%d", &pad_id) == 1) { + pad_name = req_name; + } else { + name = g_strdup_printf ("video_%d", mux->num_v_streams++); + pad_name = name; + } setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps); context = (GstMatroskaTrackContext *) g_new0 (GstMatroskaTrackVideoContext, 1); context->type = GST_MATROSKA_TRACK_TYPE_VIDEO; context->name = g_strdup ("Video"); } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) { - name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++); + /* don't mix named and unnamed pads, if the pad already exists we fail when + * trying to add it */ + if (req_name != NULL && sscanf (req_name, "subtitle_%d", &pad_id) == 1) { + pad_name = req_name; + } else { + name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++); + pad_name = name; + } setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps); context = (GstMatroskaTrackContext *) g_new0 (GstMatroskaTrackSubtitleContext, 1); @@ -1826,7 +1850,7 @@ gst_matroska_mux_request_new_pad (GstElement * element, return NULL; } - newpad = gst_pad_new_from_template (templ, name); + newpad = gst_pad_new_from_template (templ, pad_name); g_free (name); collect_pad = (GstMatroskaPad *) gst_collect_pads_add_pad_full (mux->collect, newpad, @@ -1850,10 +1874,22 @@ gst_matroska_mux_request_new_pad (GstElement * element, gst_pad_set_setcaps_function (newpad, setcapsfunc); gst_pad_set_active (newpad, TRUE); - gst_element_add_pad (element, newpad); + if (!gst_element_add_pad (element, newpad)) + goto pad_add_failed; + mux->num_streams++; + GST_DEBUG_OBJECT (newpad, "Added new request pad"); + return newpad; + + /* ERROR cases */ +pad_add_failed: + { + GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name); + gst_object_unref (newpad); + return NULL; + } } /** @@ -2025,9 +2061,9 @@ gst_matroska_mux_start (GstMatroskaMux * mux) GTimeVal time = { 0, 0 }; if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) { - ebml->caps = gst_caps_from_string ("video/webm"); + ebml->caps = gst_caps_new_simple ("video/webm", NULL); } else { - ebml->caps = gst_caps_from_string ("video/x-matroska"); + ebml->caps = gst_caps_new_simple ("video/x-matroska", NULL); } /* we start with a EBML header */ doctype = mux->doctype; @@ -2350,7 +2386,7 @@ gst_matroska_mux_finish (GstMatroskaMux * mux) * @mux: #GstMatroskaMux * @popped: True if at least one buffer was popped from #GstCollectPads * - * Find a pad with the oldest data + * Find a pad with the oldest data * (data from this pad should be written first). * * Returns: Selected pad. @@ -2397,7 +2433,7 @@ gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped) * @flags: Buffer flags. * * Create a buffer containing buffer header. - * + * * Returns: New buffer. */ static GstBuffer * diff --git a/gst/matroska/matroska-parse.c b/gst/matroska/matroska-parse.c new file mode 100644 index 0000000000..88e0e152f9 --- /dev/null +++ b/gst/matroska/matroska-parse.c @@ -0,0 +1,5058 @@ +/* GStreamer Matroska muxer/demuxer + * (c) 2003 Ronald Bultje + * (c) 2006 Tim-Philipp Müller + * (c) 2008 Sebastian Dröge + * + * matroska-parse.c: matroska file/stream parser + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* TODO: check CRC32 if present + * TODO: there can be a segment after the first segment. Handle like + * chained oggs. Fixes #334082 + * TODO: Test samples: http://www.matroska.org/samples/matrix/index.html + * http://samples.mplayerhq.hu/Matroska/ + * TODO: check if parseing is done correct for all codecs according to spec + * TODO: seeking with incomplete or without CUE + */ + +/** + * SECTION:element-matroskaparse + * + * matroskaparse parsees a Matroska file into the different contained streams. + * + * + * Example launch line + * |[ + * gst-launch -v filesrc location=/path/to/mkv ! matroskaparse ! vorbisdec ! audioconvert ! audioresample ! autoaudiosink + * ]| This pipeline parsees a Matroska file and outputs the contained Vorbis audio. + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +/* For AVI compatibility mode + and for fourcc stuff */ +#include +#include +#include + +#include + +#include + +#ifdef HAVE_ZLIB +#include +#endif + +#ifdef HAVE_BZ2 +#include +#endif + +#include + +#include "lzo.h" + +#include "matroska-parse.h" +#include "matroska-ids.h" + +GST_DEBUG_CATEGORY_STATIC (matroskaparse_debug); +#define GST_CAT_DEFAULT matroskaparse_debug + +#define DEBUG_ELEMENT_START(parse, ebml, element) \ + GST_DEBUG_OBJECT (parse, "Parsing " element " element at offset %" \ + G_GUINT64_FORMAT, gst_ebml_read_get_pos (ebml)) + +#define DEBUG_ELEMENT_STOP(parse, ebml, element, ret) \ + GST_DEBUG_OBJECT (parse, "Parsing " element " element " \ + " finished with '%s'", gst_flow_get_name (ret)) + +enum +{ + ARG_0, + ARG_METADATA, + ARG_STREAMINFO +}; + +static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-matroska; video/webm") + ); + +static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-matroska; video/webm") + ); + +static GstFlowReturn gst_matroska_parse_parse_id (GstMatroskaParse * parse, + guint32 id, guint64 length, guint needed); + +/* element functions */ +//static void gst_matroska_parse_loop (GstPad * pad); + +static gboolean gst_matroska_parse_element_send_event (GstElement * element, + GstEvent * event); +static gboolean gst_matroska_parse_element_query (GstElement * element, + GstQuery * query); + +/* pad functions */ +static gboolean gst_matroska_parse_handle_seek_event (GstMatroskaParse * parse, + GstPad * pad, GstEvent * event); +static gboolean gst_matroska_parse_handle_src_event (GstPad * pad, + GstEvent * event); +static const GstQueryType *gst_matroska_parse_get_src_query_types (GstPad * + pad); +static gboolean gst_matroska_parse_handle_src_query (GstPad * pad, + GstQuery * query); + +static gboolean gst_matroska_parse_handle_sink_event (GstPad * pad, + GstEvent * event); +static GstFlowReturn gst_matroska_parse_chain (GstPad * pad, + GstBuffer * buffer); + +static GstStateChangeReturn +gst_matroska_parse_change_state (GstElement * element, + GstStateChange transition); +static void +gst_matroska_parse_set_index (GstElement * element, GstIndex * index); +static GstIndex *gst_matroska_parse_get_index (GstElement * element); + +/* stream methods */ +static void gst_matroska_parse_reset (GstElement * element); +static gboolean perform_seek_to_offset (GstMatroskaParse * parse, + guint64 offset); + +GType gst_matroska_parse_get_type (void); +GST_BOILERPLATE (GstMatroskaParse, gst_matroska_parse, GstElement, + GST_TYPE_ELEMENT); + +static void +gst_matroska_parse_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_templ)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_templ)); + + gst_element_class_set_details_simple (element_class, "Matroska parser", + "Codec/Parser", + "Parses Matroska/WebM streams into video/audio/subtitles", + "GStreamer maintainers "); +} + +static void +gst_matroska_parse_finalize (GObject * object) +{ + GstMatroskaParse *parse = GST_MATROSKA_PARSE (object); + + if (parse->src) { + g_ptr_array_free (parse->src, TRUE); + parse->src = NULL; + } + + if (parse->global_tags) { + gst_tag_list_free (parse->global_tags); + parse->global_tags = NULL; + } + + g_object_unref (parse->adapter); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_matroska_parse_class_init (GstMatroskaParseClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + + GST_DEBUG_CATEGORY_INIT (matroskaparse_debug, "matroskaparse", 0, + "Matroska parser"); + + gobject_class->finalize = gst_matroska_parse_finalize; + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_matroska_parse_change_state); + gstelement_class->send_event = + GST_DEBUG_FUNCPTR (gst_matroska_parse_element_send_event); + gstelement_class->query = + GST_DEBUG_FUNCPTR (gst_matroska_parse_element_query); + + gstelement_class->set_index = + GST_DEBUG_FUNCPTR (gst_matroska_parse_set_index); + gstelement_class->get_index = + GST_DEBUG_FUNCPTR (gst_matroska_parse_get_index); +} + +static void +gst_matroska_parse_init (GstMatroskaParse * parse, + GstMatroskaParseClass * klass) +{ + parse->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink"); + gst_pad_set_chain_function (parse->sinkpad, + GST_DEBUG_FUNCPTR (gst_matroska_parse_chain)); + gst_pad_set_event_function (parse->sinkpad, + GST_DEBUG_FUNCPTR (gst_matroska_parse_handle_sink_event)); + gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad); + + parse->srcpad = gst_pad_new_from_static_template (&src_templ, "src"); + gst_pad_set_event_function (parse->srcpad, + GST_DEBUG_FUNCPTR (gst_matroska_parse_handle_src_event)); + gst_pad_set_query_type_function (parse->srcpad, + GST_DEBUG_FUNCPTR (gst_matroska_parse_get_src_query_types)); + gst_pad_set_query_function (parse->srcpad, + GST_DEBUG_FUNCPTR (gst_matroska_parse_handle_src_query)); + gst_pad_use_fixed_caps (parse->srcpad); + gst_pad_set_caps (parse->srcpad, gst_caps_new_simple ("video/x-matroska", + NULL)); + gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad); + + /* initial stream no. */ + parse->src = NULL; + + parse->writing_app = NULL; + parse->muxing_app = NULL; + parse->index = NULL; + parse->global_tags = NULL; + + parse->adapter = gst_adapter_new (); + + /* finish off */ + gst_matroska_parse_reset (GST_ELEMENT (parse)); +} + +static void +gst_matroska_track_free (GstMatroskaTrackContext * track) +{ + g_free (track->codec_id); + g_free (track->codec_name); + g_free (track->name); + g_free (track->language); + g_free (track->codec_priv); + g_free (track->codec_state); + + if (track->encodings != NULL) { + int i; + + for (i = 0; i < track->encodings->len; ++i) { + GstMatroskaTrackEncoding *enc = &g_array_index (track->encodings, + GstMatroskaTrackEncoding, + i); + + g_free (enc->comp_settings); + } + g_array_free (track->encodings, TRUE); + } + + if (track->pending_tags) + gst_tag_list_free (track->pending_tags); + + if (track->index_table) + g_array_free (track->index_table, TRUE); + + g_free (track); +} + +static void +gst_matroska_parse_free_parsed_el (gpointer mem, gpointer user_data) +{ + g_slice_free (guint64, mem); +} + +static void +gst_matroska_parse_reset (GstElement * element) +{ + GstMatroskaParse *parse = GST_MATROSKA_PARSE (element); + guint i; + + GST_DEBUG_OBJECT (parse, "Resetting state"); + + /* reset input */ + parse->state = GST_MATROSKA_PARSE_STATE_START; + + /* clean up existing streams */ + if (parse->src) { + g_assert (parse->src->len == parse->num_streams); + for (i = 0; i < parse->src->len; i++) { + GstMatroskaTrackContext *context = g_ptr_array_index (parse->src, i); + + gst_caps_replace (&context->caps, NULL); + gst_matroska_track_free (context); + } + g_ptr_array_free (parse->src, TRUE); + } + parse->src = g_ptr_array_new (); + + parse->num_streams = 0; + parse->num_a_streams = 0; + parse->num_t_streams = 0; + parse->num_v_streams = 0; + + /* reset media info */ + g_free (parse->writing_app); + parse->writing_app = NULL; + g_free (parse->muxing_app); + parse->muxing_app = NULL; + + /* reset indexes */ + if (parse->index) { + g_array_free (parse->index, TRUE); + parse->index = NULL; + } + + /* reset timers */ + parse->clock = NULL; + parse->time_scale = 1000000; + parse->created = G_MININT64; + + parse->index_parsed = FALSE; + parse->tracks_parsed = FALSE; + parse->segmentinfo_parsed = FALSE; + parse->attachments_parsed = FALSE; + + g_list_foreach (parse->tags_parsed, + (GFunc) gst_matroska_parse_free_parsed_el, NULL); + g_list_free (parse->tags_parsed); + parse->tags_parsed = NULL; + + g_list_foreach (parse->seek_parsed, + (GFunc) gst_matroska_parse_free_parsed_el, NULL); + g_list_free (parse->seek_parsed); + parse->seek_parsed = NULL; + + gst_segment_init (&parse->segment, GST_FORMAT_TIME); + parse->last_stop_end = GST_CLOCK_TIME_NONE; + parse->seek_block = 0; + + parse->offset = 0; + parse->cluster_time = GST_CLOCK_TIME_NONE; + parse->cluster_offset = 0; + parse->next_cluster_offset = 0; + parse->index_offset = 0; + parse->seekable = FALSE; + parse->need_newsegment = FALSE; + parse->building_index = FALSE; + if (parse->seek_event) { + gst_event_unref (parse->seek_event); + parse->seek_event = NULL; + } + + parse->seek_index = NULL; + parse->seek_entry = 0; + + if (parse->close_segment) { + gst_event_unref (parse->close_segment); + parse->close_segment = NULL; + } + + if (parse->new_segment) { + gst_event_unref (parse->new_segment); + parse->new_segment = NULL; + } + + if (parse->element_index) { + gst_object_unref (parse->element_index); + parse->element_index = NULL; + } + parse->element_index_writer_id = -1; + + if (parse->global_tags) { + gst_tag_list_free (parse->global_tags); + } + parse->global_tags = gst_tag_list_new (); + + if (parse->cached_buffer) { + gst_buffer_unref (parse->cached_buffer); + parse->cached_buffer = NULL; + } +} + +/* + * Calls pull_range for (offset,size) without advancing our offset + */ +static GstFlowReturn +gst_matroska_parse_peek_bytes (GstMatroskaParse * parse, guint64 offset, + guint size, GstBuffer ** p_buf, guint8 ** bytes) +{ + GstFlowReturn ret; + + /* Caching here actually makes much less difference than one would expect. + * We do it mainly to avoid pulling buffers of 1 byte all the time */ + if (parse->cached_buffer) { + guint64 cache_offset = GST_BUFFER_OFFSET (parse->cached_buffer); + guint cache_size = GST_BUFFER_SIZE (parse->cached_buffer); + + if (cache_offset <= parse->offset && + (parse->offset + size) <= (cache_offset + cache_size)) { + if (p_buf) + *p_buf = gst_buffer_create_sub (parse->cached_buffer, + parse->offset - cache_offset, size); + if (bytes) + *bytes = GST_BUFFER_DATA (parse->cached_buffer) + parse->offset - + cache_offset; + return GST_FLOW_OK; + } + /* not enough data in the cache, free cache and get a new one */ + gst_buffer_unref (parse->cached_buffer); + parse->cached_buffer = NULL; + } + + /* refill the cache */ + ret = gst_pad_pull_range (parse->sinkpad, parse->offset, + MAX (size, 64 * 1024), &parse->cached_buffer); + if (ret != GST_FLOW_OK) { + parse->cached_buffer = NULL; + return ret; + } + + if (GST_BUFFER_SIZE (parse->cached_buffer) >= size) { + if (p_buf) + *p_buf = gst_buffer_create_sub (parse->cached_buffer, 0, size); + if (bytes) + *bytes = GST_BUFFER_DATA (parse->cached_buffer); + return GST_FLOW_OK; + } + + /* Not possible to get enough data, try a last time with + * requesting exactly the size we need */ + gst_buffer_unref (parse->cached_buffer); + parse->cached_buffer = NULL; + + ret = + gst_pad_pull_range (parse->sinkpad, parse->offset, size, + &parse->cached_buffer); + if (ret != GST_FLOW_OK) { + GST_DEBUG_OBJECT (parse, "pull_range returned %d", ret); + if (p_buf) + *p_buf = NULL; + if (bytes) + *bytes = NULL; + return ret; + } + + if (GST_BUFFER_SIZE (parse->cached_buffer) < size) { + GST_WARNING_OBJECT (parse, "Dropping short buffer at offset %" + G_GUINT64_FORMAT ": wanted %u bytes, got %u bytes", parse->offset, + size, GST_BUFFER_SIZE (parse->cached_buffer)); + + gst_buffer_unref (parse->cached_buffer); + parse->cached_buffer = NULL; + if (p_buf) + *p_buf = NULL; + if (bytes) + *bytes = NULL; + return GST_FLOW_UNEXPECTED; + } + + if (p_buf) + *p_buf = gst_buffer_create_sub (parse->cached_buffer, 0, size); + if (bytes) + *bytes = GST_BUFFER_DATA (parse->cached_buffer); + + return GST_FLOW_OK; +} + +static const guint8 * +gst_matroska_parse_peek_pull (GstMatroskaParse * parse, guint peek) +{ + guint8 *data = NULL; + + gst_matroska_parse_peek_bytes (parse, parse->offset, peek, NULL, &data); + return data; +} + +static GstFlowReturn +gst_matroska_parse_peek_id_length_pull (GstMatroskaParse * parse, guint32 * _id, + guint64 * _length, guint * _needed) +{ + return gst_ebml_peek_id_length (_id, _length, _needed, + (GstPeekData) gst_matroska_parse_peek_pull, (gpointer) parse, + GST_ELEMENT_CAST (parse), parse->offset); +} + +static gint64 +gst_matroska_parse_get_length (GstMatroskaParse * parse) +{ + GstFormat fmt = GST_FORMAT_BYTES; + gint64 end = -1; + + if (!gst_pad_query_peer_duration (parse->sinkpad, &fmt, &end) || + fmt != GST_FORMAT_BYTES || end < 0) + GST_DEBUG_OBJECT (parse, "no upstream length"); + + return end; +} + +static gint +gst_matroska_parse_stream_from_num (GstMatroskaParse * parse, guint track_num) +{ + guint n; + + g_assert (parse->src->len == parse->num_streams); + for (n = 0; n < parse->src->len; n++) { + GstMatroskaTrackContext *context = g_ptr_array_index (parse->src, n); + + if (context->num == track_num) { + return n; + } + } + + if (n == parse->num_streams) + GST_WARNING_OBJECT (parse, + "Failed to find corresponding pad for tracknum %d", track_num); + + return -1; +} + +static gint +gst_matroska_parse_encoding_cmp (GstMatroskaTrackEncoding * a, + GstMatroskaTrackEncoding * b) +{ + if (b->order > a->order) + return 1; + else if (b->order < a->order) + return -1; + else + return 0; +} + +static gboolean +gst_matroska_parse_encoding_order_unique (GArray * encodings, guint64 order) +{ + gint i; + + if (encodings == NULL || encodings->len == 0) + return TRUE; + + for (i = 0; i < encodings->len; i++) + if (g_array_index (encodings, GstMatroskaTrackEncoding, i).order == order) + return FALSE; + + return TRUE; +} + +static GstFlowReturn +gst_matroska_parse_read_track_encoding (GstMatroskaParse * parse, + GstEbmlRead * ebml, GstMatroskaTrackContext * context) +{ + GstMatroskaTrackEncoding enc = { 0, }; + GstFlowReturn ret; + guint32 id; + + DEBUG_ELEMENT_START (parse, ebml, "ContentEncoding"); + /* Set default values */ + enc.scope = 1; + /* All other default values are 0 */ + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "ContentEncoding", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + case GST_MATROSKA_ID_CONTENTENCODINGORDER:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (!gst_matroska_parse_encoding_order_unique (context->encodings, num)) { + GST_ERROR_OBJECT (parse, "ContentEncodingOrder %" G_GUINT64_FORMAT + "is not unique for track %d", num, context->num); + ret = GST_FLOW_ERROR; + break; + } + + GST_DEBUG_OBJECT (parse, "ContentEncodingOrder: %" G_GUINT64_FORMAT, + num); + enc.order = num; + break; + } + case GST_MATROSKA_ID_CONTENTENCODINGSCOPE:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num > 7 && num == 0) { + GST_ERROR_OBJECT (parse, "Invalid ContentEncodingScope %" + G_GUINT64_FORMAT, num); + ret = GST_FLOW_ERROR; + break; + } + + GST_DEBUG_OBJECT (parse, "ContentEncodingScope: %" G_GUINT64_FORMAT, + num); + enc.scope = num; + + break; + } + case GST_MATROSKA_ID_CONTENTENCODINGTYPE:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num > 1) { + GST_ERROR_OBJECT (parse, "Invalid ContentEncodingType %" + G_GUINT64_FORMAT, num); + ret = GST_FLOW_ERROR; + break; + } else if (num != 0) { + GST_ERROR_OBJECT (parse, "Encrypted tracks are not supported yet"); + ret = GST_FLOW_ERROR; + break; + } + GST_DEBUG_OBJECT (parse, "ContentEncodingType: %" G_GUINT64_FORMAT, + num); + enc.type = num; + break; + } + case GST_MATROSKA_ID_CONTENTCOMPRESSION:{ + + DEBUG_ELEMENT_START (parse, ebml, "ContentCompression"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) + break; + + while (ret == GST_FLOW_OK && + gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + case GST_MATROSKA_ID_CONTENTCOMPALGO:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) { + break; + } + if (num > 3) { + GST_ERROR_OBJECT (parse, "Invalid ContentCompAlgo %" + G_GUINT64_FORMAT, num); + ret = GST_FLOW_ERROR; + break; + } + GST_DEBUG_OBJECT (parse, "ContentCompAlgo: %" G_GUINT64_FORMAT, + num); + enc.comp_algo = num; + + break; + } + case GST_MATROSKA_ID_CONTENTCOMPSETTINGS:{ + guint8 *data; + guint64 size; + + if ((ret = + gst_ebml_read_binary (ebml, &id, &data, + &size)) != GST_FLOW_OK) { + break; + } + enc.comp_settings = data; + enc.comp_settings_length = size; + GST_DEBUG_OBJECT (parse, + "ContentCompSettings of size %" G_GUINT64_FORMAT, size); + break; + } + default: + GST_WARNING_OBJECT (parse, + "Unknown ContentCompression subelement 0x%x - ignoring", id); + ret = gst_ebml_read_skip (ebml); + break; + } + } + DEBUG_ELEMENT_STOP (parse, ebml, "ContentCompression", ret); + break; + } + + case GST_MATROSKA_ID_CONTENTENCRYPTION: + GST_ERROR_OBJECT (parse, "Encrypted tracks not yet supported"); + gst_ebml_read_skip (ebml); + ret = GST_FLOW_ERROR; + break; + default: + GST_WARNING_OBJECT (parse, + "Unknown ContentEncoding subelement 0x%x - ignoring", id); + ret = gst_ebml_read_skip (ebml); + break; + } + } + + DEBUG_ELEMENT_STOP (parse, ebml, "ContentEncoding", ret); + if (ret != GST_FLOW_OK && ret != GST_FLOW_UNEXPECTED) + return ret; + + /* TODO: Check if the combination of values is valid */ + + g_array_append_val (context->encodings, enc); + + return ret; +} + +static gboolean +gst_matroska_decompress_data (GstMatroskaTrackEncoding * enc, + guint8 ** data_out, guint * size_out, + GstMatroskaTrackCompressionAlgorithm algo) +{ + guint8 *new_data = NULL; + guint new_size = 0; + guint8 *data = *data_out; + guint size = *size_out; + gboolean ret = TRUE; + + if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_ZLIB) { +#ifdef HAVE_ZLIB + /* zlib encoded data */ + z_stream zstream; + guint orig_size; + int result; + + orig_size = size; + zstream.zalloc = (alloc_func) 0; + zstream.zfree = (free_func) 0; + zstream.opaque = (voidpf) 0; + if (inflateInit (&zstream) != Z_OK) { + GST_WARNING ("zlib initialization failed."); + ret = FALSE; + goto out; + } + zstream.next_in = (Bytef *) data; + zstream.avail_in = orig_size; + new_size = orig_size; + new_data = g_malloc (new_size); + zstream.avail_out = new_size; + zstream.next_out = (Bytef *) new_data; + + do { + result = inflate (&zstream, Z_NO_FLUSH); + if (result != Z_OK && result != Z_STREAM_END) { + GST_WARNING ("zlib decompression failed."); + g_free (new_data); + inflateEnd (&zstream); + break; + } + new_size += 4000; + new_data = g_realloc (new_data, new_size); + zstream.next_out = (Bytef *) (new_data + zstream.total_out); + zstream.avail_out += 4000; + } while (zstream.avail_in != 0 && result != Z_STREAM_END); + + if (result != Z_STREAM_END) { + ret = FALSE; + goto out; + } else { + new_size = zstream.total_out; + inflateEnd (&zstream); + } +#else + GST_WARNING ("zlib encoded tracks not supported."); + ret = FALSE; + goto out; +#endif + } else if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_BZLIB) { +#ifdef HAVE_BZ2 + /* bzip2 encoded data */ + bz_stream bzstream; + guint orig_size; + int result; + + bzstream.bzalloc = NULL; + bzstream.bzfree = NULL; + bzstream.opaque = NULL; + orig_size = size; + + if (BZ2_bzDecompressInit (&bzstream, 0, 0) != BZ_OK) { + GST_WARNING ("bzip2 initialization failed."); + ret = FALSE; + goto out; + } + + bzstream.next_in = (char *) data; + bzstream.avail_in = orig_size; + new_size = orig_size; + new_data = g_malloc (new_size); + bzstream.avail_out = new_size; + bzstream.next_out = (char *) new_data; + + do { + result = BZ2_bzDecompress (&bzstream); + if (result != BZ_OK && result != BZ_STREAM_END) { + GST_WARNING ("bzip2 decompression failed."); + g_free (new_data); + BZ2_bzDecompressEnd (&bzstream); + break; + } + new_size += 4000; + new_data = g_realloc (new_data, new_size); + bzstream.next_out = (char *) (new_data + bzstream.total_out_lo32); + bzstream.avail_out += 4000; + } while (bzstream.avail_in != 0 && result != BZ_STREAM_END); + + if (result != BZ_STREAM_END) { + ret = FALSE; + goto out; + } else { + new_size = bzstream.total_out_lo32; + BZ2_bzDecompressEnd (&bzstream); + } +#else + GST_WARNING ("bzip2 encoded tracks not supported."); + ret = FALSE; + goto out; +#endif + } else if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_LZO1X) { + /* lzo encoded data */ + int result; + int orig_size, out_size; + + orig_size = size; + out_size = size; + new_size = size; + new_data = g_malloc (new_size); + + do { + orig_size = size; + out_size = new_size; + + result = lzo1x_decode (new_data, &out_size, data, &orig_size); + + if (orig_size > 0) { + new_size += 4000; + new_data = g_realloc (new_data, new_size); + } + } while (orig_size > 0 && result == LZO_OUTPUT_FULL); + + new_size -= out_size; + + if (result != LZO_OUTPUT_FULL) { + GST_WARNING ("lzo decompression failed"); + g_free (new_data); + + ret = FALSE; + goto out; + } + + } else if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_HEADERSTRIP) { + /* header stripped encoded data */ + if (enc->comp_settings_length > 0) { + new_data = g_malloc (size + enc->comp_settings_length); + new_size = size + enc->comp_settings_length; + + memcpy (new_data, enc->comp_settings, enc->comp_settings_length); + memcpy (new_data + enc->comp_settings_length, data, size); + } + } else { + GST_ERROR ("invalid compression algorithm %d", algo); + ret = FALSE; + } + +out: + + if (!ret) { + *data_out = NULL; + *size_out = 0; + } else { + *data_out = new_data; + *size_out = new_size; + } + + return ret; +} + +static gboolean +gst_matroska_decode_data (GArray * encodings, guint8 ** data_out, + guint * size_out, GstMatroskaTrackEncodingScope scope, gboolean free) +{ + guint8 *data; + guint size; + gboolean ret = TRUE; + gint i; + + g_return_val_if_fail (encodings != NULL, FALSE); + g_return_val_if_fail (data_out != NULL && *data_out != NULL, FALSE); + g_return_val_if_fail (size_out != NULL, FALSE); + + data = *data_out; + size = *size_out; + + for (i = 0; i < encodings->len; i++) { + GstMatroskaTrackEncoding *enc = + &g_array_index (encodings, GstMatroskaTrackEncoding, i); + guint8 *new_data = NULL; + guint new_size = 0; + + if ((enc->scope & scope) == 0) + continue; + + /* Encryption not supported yet */ + if (enc->type != 0) { + ret = FALSE; + break; + } + + new_data = data; + new_size = size; + + ret = + gst_matroska_decompress_data (enc, &new_data, &new_size, + enc->comp_algo); + + if (!ret) + break; + + if ((data == *data_out && free) || (data != *data_out)) + g_free (data); + + data = new_data; + size = new_size; + } + + if (!ret) { + if ((data == *data_out && free) || (data != *data_out)) + g_free (data); + + *data_out = NULL; + *size_out = 0; + } else { + *data_out = data; + *size_out = size; + } + + return ret; +} + +static GstFlowReturn +gst_matroska_decode_content_encodings (GArray * encodings) +{ + gint i; + + if (encodings == NULL) + return GST_FLOW_OK; + + for (i = 0; i < encodings->len; i++) { + GstMatroskaTrackEncoding *enc = + &g_array_index (encodings, GstMatroskaTrackEncoding, i); + GstMatroskaTrackEncoding *enc2; + guint8 *data = NULL; + guint size; + + if ((enc->scope & GST_MATROSKA_TRACK_ENCODING_SCOPE_NEXT_CONTENT_ENCODING) + == 0) + continue; + + /* Encryption not supported yet */ + if (enc->type != 0) + return GST_FLOW_ERROR; + + if (i + 1 >= encodings->len) + return GST_FLOW_ERROR; + + enc2 = &g_array_index (encodings, GstMatroskaTrackEncoding, i + 1); + + if (enc->comp_settings_length == 0) + continue; + + data = enc->comp_settings; + size = enc->comp_settings_length; + + if (!gst_matroska_decompress_data (enc, &data, &size, enc->comp_algo)) + return GST_FLOW_ERROR; + + g_free (enc->comp_settings); + + enc->comp_settings = data; + enc->comp_settings_length = size; + } + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_matroska_parse_read_track_encodings (GstMatroskaParse * parse, + GstEbmlRead * ebml, GstMatroskaTrackContext * context) +{ + GstFlowReturn ret; + guint32 id; + + DEBUG_ELEMENT_START (parse, ebml, "ContentEncodings"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "ContentEncodings", ret); + return ret; + } + + context->encodings = + g_array_sized_new (FALSE, FALSE, sizeof (GstMatroskaTrackEncoding), 1); + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + case GST_MATROSKA_ID_CONTENTENCODING: + ret = gst_matroska_parse_read_track_encoding (parse, ebml, context); + break; + default: + GST_WARNING_OBJECT (parse, + "Unknown ContentEncodings subelement 0x%x - ignoring", id); + ret = gst_ebml_read_skip (ebml); + break; + } + } + + DEBUG_ELEMENT_STOP (parse, ebml, "ContentEncodings", ret); + if (ret != GST_FLOW_OK && ret != GST_FLOW_UNEXPECTED) + return ret; + + /* Sort encodings according to their order */ + g_array_sort (context->encodings, + (GCompareFunc) gst_matroska_parse_encoding_cmp); + + return gst_matroska_decode_content_encodings (context->encodings); +} + +static gboolean +gst_matroska_parse_tracknumber_unique (GstMatroskaParse * parse, guint64 num) +{ + gint i; + + g_assert (parse->src->len == parse->num_streams); + for (i = 0; i < parse->src->len; i++) { + GstMatroskaTrackContext *context = g_ptr_array_index (parse->src, i); + + if (context->num == num) + return FALSE; + } + + return TRUE; +} + +static GstFlowReturn +gst_matroska_parse_add_stream (GstMatroskaParse * parse, GstEbmlRead * ebml) +{ + GstMatroskaTrackContext *context; + GstFlowReturn ret; + guint32 id; + + DEBUG_ELEMENT_START (parse, ebml, "TrackEntry"); + + /* start with the master */ + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "TrackEntry", ret); + return ret; + } + + /* allocate generic... if we know the type, we'll g_renew() + * with the precise type */ + context = g_new0 (GstMatroskaTrackContext, 1); + g_ptr_array_add (parse->src, context); + context->index = parse->num_streams; + context->index_writer_id = -1; + context->type = 0; /* no type yet */ + context->default_duration = 0; + context->pos = 0; + context->set_discont = TRUE; + context->timecodescale = 1.0; + context->flags = + GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT | + GST_MATROSKA_TRACK_LACING; + context->last_flow = GST_FLOW_OK; + context->to_offset = G_MAXINT64; + parse->num_streams++; + g_assert (parse->src->len == parse->num_streams); + + GST_DEBUG_OBJECT (parse, "Stream number %d", context->index); + + /* try reading the trackentry headers */ + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + /* track number (unique stream ID) */ + case GST_MATROSKA_ID_TRACKNUMBER:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num == 0) { + GST_ERROR_OBJECT (parse, "Invalid TrackNumber 0"); + ret = GST_FLOW_ERROR; + break; + } else if (!gst_matroska_parse_tracknumber_unique (parse, num)) { + GST_ERROR_OBJECT (parse, "TrackNumber %" G_GUINT64_FORMAT + " is not unique", num); + ret = GST_FLOW_ERROR; + break; + } + + GST_DEBUG_OBJECT (parse, "TrackNumber: %" G_GUINT64_FORMAT, num); + context->num = num; + break; + } + /* track UID (unique identifier) */ + case GST_MATROSKA_ID_TRACKUID:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num == 0) { + GST_ERROR_OBJECT (parse, "Invalid TrackUID 0"); + ret = GST_FLOW_ERROR; + break; + } + + GST_DEBUG_OBJECT (parse, "TrackUID: %" G_GUINT64_FORMAT, num); + context->uid = num; + break; + } + + /* track type (video, audio, combined, subtitle, etc.) */ + case GST_MATROSKA_ID_TRACKTYPE:{ + guint64 track_type; + + if ((ret = gst_ebml_read_uint (ebml, &id, &track_type)) != GST_FLOW_OK) { + break; + } + + if (context->type != 0 && context->type != track_type) { + GST_WARNING_OBJECT (parse, + "More than one tracktype defined in a TrackEntry - skipping"); + break; + } else if (track_type < 1 || track_type > 254) { + GST_WARNING_OBJECT (parse, "Invalid TrackType %" G_GUINT64_FORMAT, + track_type); + break; + } + + GST_DEBUG_OBJECT (parse, "TrackType: %" G_GUINT64_FORMAT, track_type); + + /* ok, so we're actually going to reallocate this thing */ + switch (track_type) { + case GST_MATROSKA_TRACK_TYPE_VIDEO: + gst_matroska_track_init_video_context (&context); + break; + case GST_MATROSKA_TRACK_TYPE_AUDIO: + gst_matroska_track_init_audio_context (&context); + break; + case GST_MATROSKA_TRACK_TYPE_SUBTITLE: + gst_matroska_track_init_subtitle_context (&context); + break; + case GST_MATROSKA_TRACK_TYPE_COMPLEX: + case GST_MATROSKA_TRACK_TYPE_LOGO: + case GST_MATROSKA_TRACK_TYPE_BUTTONS: + case GST_MATROSKA_TRACK_TYPE_CONTROL: + default: + GST_WARNING_OBJECT (parse, + "Unknown or unsupported TrackType %" G_GUINT64_FORMAT, + track_type); + context->type = 0; + break; + } + g_ptr_array_index (parse->src, parse->num_streams - 1) = context; + break; + } + + /* tracktype specific stuff for video */ + case GST_MATROSKA_ID_TRACKVIDEO:{ + GstMatroskaTrackVideoContext *videocontext; + + DEBUG_ELEMENT_START (parse, ebml, "TrackVideo"); + + if (!gst_matroska_track_init_video_context (&context)) { + GST_WARNING_OBJECT (parse, + "TrackVideo element in non-video track - ignoring track"); + ret = GST_FLOW_ERROR; + break; + } else if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + break; + } + videocontext = (GstMatroskaTrackVideoContext *) context; + g_ptr_array_index (parse->src, parse->num_streams - 1) = context; + + while (ret == GST_FLOW_OK && + gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + /* Should be one level up but some broken muxers write it here. */ + case GST_MATROSKA_ID_TRACKDEFAULTDURATION:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num == 0) { + GST_WARNING_OBJECT (parse, "Invalid TrackDefaultDuration 0"); + break; + } + + GST_DEBUG_OBJECT (parse, + "TrackDefaultDuration: %" G_GUINT64_FORMAT, num); + context->default_duration = num; + break; + } + + /* video framerate */ + /* NOTE: This one is here only for backward compatibility. + * Use _TRACKDEFAULDURATION one level up. */ + case GST_MATROSKA_ID_VIDEOFRAMERATE:{ + gdouble num; + + if ((ret = gst_ebml_read_float (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num <= 0.0) { + GST_WARNING_OBJECT (parse, "Invalid TrackVideoFPS %lf", num); + break; + } + + GST_DEBUG_OBJECT (parse, "TrackVideoFrameRate: %lf", num); + if (context->default_duration == 0) + context->default_duration = + gst_gdouble_to_guint64 ((gdouble) GST_SECOND * (1.0 / num)); + videocontext->default_fps = num; + break; + } + + /* width of the size to display the video at */ + case GST_MATROSKA_ID_VIDEODISPLAYWIDTH:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num == 0) { + GST_WARNING_OBJECT (parse, "Invalid TrackVideoDisplayWidth 0"); + break; + } + + GST_DEBUG_OBJECT (parse, + "TrackVideoDisplayWidth: %" G_GUINT64_FORMAT, num); + videocontext->display_width = num; + break; + } + + /* height of the size to display the video at */ + case GST_MATROSKA_ID_VIDEODISPLAYHEIGHT:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num == 0) { + GST_WARNING_OBJECT (parse, "Invalid TrackVideoDisplayHeight 0"); + break; + } + + GST_DEBUG_OBJECT (parse, + "TrackVideoDisplayHeight: %" G_GUINT64_FORMAT, num); + videocontext->display_height = num; + break; + } + + /* width of the video in the file */ + case GST_MATROSKA_ID_VIDEOPIXELWIDTH:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num == 0) { + GST_WARNING_OBJECT (parse, "Invalid TrackVideoPixelWidth 0"); + break; + } + + GST_DEBUG_OBJECT (parse, + "TrackVideoPixelWidth: %" G_GUINT64_FORMAT, num); + videocontext->pixel_width = num; + break; + } + + /* height of the video in the file */ + case GST_MATROSKA_ID_VIDEOPIXELHEIGHT:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num == 0) { + GST_WARNING_OBJECT (parse, "Invalid TrackVideoPixelHeight 0"); + break; + } + + GST_DEBUG_OBJECT (parse, + "TrackVideoPixelHeight: %" G_GUINT64_FORMAT, num); + videocontext->pixel_height = num; + break; + } + + /* whether the video is interlaced */ + case GST_MATROSKA_ID_VIDEOFLAGINTERLACED:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num) + context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED; + else + context->flags &= ~GST_MATROSKA_VIDEOTRACK_INTERLACED; + GST_DEBUG_OBJECT (parse, "TrackVideoInterlaced: %d", + (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED) ? 1 : + 0); + break; + } + + /* aspect ratio behaviour */ + case GST_MATROSKA_ID_VIDEOASPECTRATIOTYPE:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num != GST_MATROSKA_ASPECT_RATIO_MODE_FREE && + num != GST_MATROSKA_ASPECT_RATIO_MODE_KEEP && + num != GST_MATROSKA_ASPECT_RATIO_MODE_FIXED) { + GST_WARNING_OBJECT (parse, + "Unknown TrackVideoAspectRatioType 0x%x", (guint) num); + break; + } + GST_DEBUG_OBJECT (parse, + "TrackVideoAspectRatioType: %" G_GUINT64_FORMAT, num); + videocontext->asr_mode = num; + break; + } + + /* colourspace (only matters for raw video) fourcc */ + case GST_MATROSKA_ID_VIDEOCOLOURSPACE:{ + guint8 *data; + guint64 datalen; + + if ((ret = + gst_ebml_read_binary (ebml, &id, &data, + &datalen)) != GST_FLOW_OK) + break; + + if (datalen != 4) { + g_free (data); + GST_WARNING_OBJECT (parse, + "Invalid TrackVideoColourSpace length %" G_GUINT64_FORMAT, + datalen); + break; + } + + memcpy (&videocontext->fourcc, data, 4); + GST_DEBUG_OBJECT (parse, + "TrackVideoColourSpace: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (videocontext->fourcc)); + g_free (data); + break; + } + + default: + GST_WARNING_OBJECT (parse, + "Unknown TrackVideo subelement 0x%x - ignoring", id); + /* fall through */ + case GST_MATROSKA_ID_VIDEOSTEREOMODE: + case GST_MATROSKA_ID_VIDEODISPLAYUNIT: + case GST_MATROSKA_ID_VIDEOPIXELCROPBOTTOM: + case GST_MATROSKA_ID_VIDEOPIXELCROPTOP: + case GST_MATROSKA_ID_VIDEOPIXELCROPLEFT: + case GST_MATROSKA_ID_VIDEOPIXELCROPRIGHT: + case GST_MATROSKA_ID_VIDEOGAMMAVALUE: + ret = gst_ebml_read_skip (ebml); + break; + } + } + + DEBUG_ELEMENT_STOP (parse, ebml, "TrackVideo", ret); + break; + } + + /* tracktype specific stuff for audio */ + case GST_MATROSKA_ID_TRACKAUDIO:{ + GstMatroskaTrackAudioContext *audiocontext; + + DEBUG_ELEMENT_START (parse, ebml, "TrackAudio"); + + if (!gst_matroska_track_init_audio_context (&context)) { + GST_WARNING_OBJECT (parse, + "TrackAudio element in non-audio track - ignoring track"); + ret = GST_FLOW_ERROR; + break; + } + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) + break; + + audiocontext = (GstMatroskaTrackAudioContext *) context; + g_ptr_array_index (parse->src, parse->num_streams - 1) = context; + + while (ret == GST_FLOW_OK && + gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + /* samplerate */ + case GST_MATROSKA_ID_AUDIOSAMPLINGFREQ:{ + gdouble num; + + if ((ret = gst_ebml_read_float (ebml, &id, &num)) != GST_FLOW_OK) + break; + + + if (num <= 0.0) { + GST_WARNING_OBJECT (parse, + "Invalid TrackAudioSamplingFrequency %lf", num); + break; + } + + GST_DEBUG_OBJECT (parse, "TrackAudioSamplingFrequency: %lf", num); + audiocontext->samplerate = num; + break; + } + + /* bitdepth */ + case GST_MATROSKA_ID_AUDIOBITDEPTH:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num == 0) { + GST_WARNING_OBJECT (parse, "Invalid TrackAudioBitDepth 0"); + break; + } + + GST_DEBUG_OBJECT (parse, "TrackAudioBitDepth: %" G_GUINT64_FORMAT, + num); + audiocontext->bitdepth = num; + break; + } + + /* channels */ + case GST_MATROSKA_ID_AUDIOCHANNELS:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num == 0) { + GST_WARNING_OBJECT (parse, "Invalid TrackAudioChannels 0"); + break; + } + + GST_DEBUG_OBJECT (parse, "TrackAudioChannels: %" G_GUINT64_FORMAT, + num); + audiocontext->channels = num; + break; + } + + default: + GST_WARNING_OBJECT (parse, + "Unknown TrackAudio subelement 0x%x - ignoring", id); + /* fall through */ + case GST_MATROSKA_ID_AUDIOCHANNELPOSITIONS: + case GST_MATROSKA_ID_AUDIOOUTPUTSAMPLINGFREQ: + ret = gst_ebml_read_skip (ebml); + break; + } + } + + DEBUG_ELEMENT_STOP (parse, ebml, "TrackAudio", ret); + + break; + } + + /* codec identifier */ + case GST_MATROSKA_ID_CODECID:{ + gchar *text; + + if ((ret = gst_ebml_read_ascii (ebml, &id, &text)) != GST_FLOW_OK) + break; + + GST_DEBUG_OBJECT (parse, "CodecID: %s", GST_STR_NULL (text)); + context->codec_id = text; + break; + } + + /* codec private data */ + case GST_MATROSKA_ID_CODECPRIVATE:{ + guint8 *data; + guint64 size; + + if ((ret = + gst_ebml_read_binary (ebml, &id, &data, &size)) != GST_FLOW_OK) + break; + + context->codec_priv = data; + context->codec_priv_size = size; + + GST_DEBUG_OBJECT (parse, "CodecPrivate of size %" G_GUINT64_FORMAT, + size); + break; + } + + /* name of the codec */ + case GST_MATROSKA_ID_CODECNAME:{ + gchar *text; + + if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK) + break; + + GST_DEBUG_OBJECT (parse, "CodecName: %s", GST_STR_NULL (text)); + context->codec_name = text; + break; + } + + /* name of this track */ + case GST_MATROSKA_ID_TRACKNAME:{ + gchar *text; + + if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK) + break; + + context->name = text; + GST_DEBUG_OBJECT (parse, "TrackName: %s", GST_STR_NULL (text)); + break; + } + + /* language (matters for audio/subtitles, mostly) */ + case GST_MATROSKA_ID_TRACKLANGUAGE:{ + gchar *text; + + if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK) + break; + + + context->language = text; + + /* fre-ca => fre */ + if (strlen (context->language) >= 4 && context->language[3] == '-') + context->language[3] = '\0'; + + GST_DEBUG_OBJECT (parse, "TrackLanguage: %s", + GST_STR_NULL (context->language)); + break; + } + + /* whether this is actually used */ + case GST_MATROSKA_ID_TRACKFLAGENABLED:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num) + context->flags |= GST_MATROSKA_TRACK_ENABLED; + else + context->flags &= ~GST_MATROSKA_TRACK_ENABLED; + + GST_DEBUG_OBJECT (parse, "TrackEnabled: %d", + (context->flags & GST_MATROSKA_TRACK_ENABLED) ? 1 : 0); + break; + } + + /* whether it's the default for this track type */ + case GST_MATROSKA_ID_TRACKFLAGDEFAULT:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num) + context->flags |= GST_MATROSKA_TRACK_DEFAULT; + else + context->flags &= ~GST_MATROSKA_TRACK_DEFAULT; + + GST_DEBUG_OBJECT (parse, "TrackDefault: %d", + (context->flags & GST_MATROSKA_TRACK_ENABLED) ? 1 : 0); + break; + } + + /* whether the track must be used during playback */ + case GST_MATROSKA_ID_TRACKFLAGFORCED:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num) + context->flags |= GST_MATROSKA_TRACK_FORCED; + else + context->flags &= ~GST_MATROSKA_TRACK_FORCED; + + GST_DEBUG_OBJECT (parse, "TrackForced: %d", + (context->flags & GST_MATROSKA_TRACK_ENABLED) ? 1 : 0); + break; + } + + /* lacing (like MPEG, where blocks don't end/start on frame + * boundaries) */ + case GST_MATROSKA_ID_TRACKFLAGLACING:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num) + context->flags |= GST_MATROSKA_TRACK_LACING; + else + context->flags &= ~GST_MATROSKA_TRACK_LACING; + + GST_DEBUG_OBJECT (parse, "TrackLacing: %d", + (context->flags & GST_MATROSKA_TRACK_ENABLED) ? 1 : 0); + break; + } + + /* default length (in time) of one data block in this track */ + case GST_MATROSKA_ID_TRACKDEFAULTDURATION:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + + if (num == 0) { + GST_WARNING_OBJECT (parse, "Invalid TrackDefaultDuration 0"); + break; + } + + GST_DEBUG_OBJECT (parse, "TrackDefaultDuration: %" G_GUINT64_FORMAT, + num); + context->default_duration = num; + break; + } + + case GST_MATROSKA_ID_CONTENTENCODINGS:{ + ret = gst_matroska_parse_read_track_encodings (parse, ebml, context); + break; + } + + case GST_MATROSKA_ID_TRACKTIMECODESCALE:{ + gdouble num; + + if ((ret = gst_ebml_read_float (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num <= 0.0) { + GST_WARNING_OBJECT (parse, "Invalid TrackTimeCodeScale %lf", num); + break; + } + + GST_DEBUG_OBJECT (parse, "TrackTimeCodeScale: %lf", num); + context->timecodescale = num; + break; + } + + default: + GST_WARNING ("Unknown TrackEntry subelement 0x%x - ignoring", id); + /* pass-through */ + + /* we ignore these because they're nothing useful (i.e. crap) + * or simply not implemented yet. */ + case GST_MATROSKA_ID_TRACKMINCACHE: + case GST_MATROSKA_ID_TRACKMAXCACHE: + case GST_MATROSKA_ID_MAXBLOCKADDITIONID: + case GST_MATROSKA_ID_TRACKATTACHMENTLINK: + case GST_MATROSKA_ID_TRACKOVERLAY: + case GST_MATROSKA_ID_TRACKTRANSLATE: + case GST_MATROSKA_ID_TRACKOFFSET: + case GST_MATROSKA_ID_CODECSETTINGS: + case GST_MATROSKA_ID_CODECINFOURL: + case GST_MATROSKA_ID_CODECDOWNLOADURL: + case GST_MATROSKA_ID_CODECDECODEALL: + ret = gst_ebml_read_skip (ebml); + break; + } + } + + DEBUG_ELEMENT_STOP (parse, ebml, "TrackEntry", ret); + + /* Decode codec private data if necessary */ + if (context->encodings && context->encodings->len > 0 && context->codec_priv + && context->codec_priv_size > 0) { + if (!gst_matroska_decode_data (context->encodings, + &context->codec_priv, &context->codec_priv_size, + GST_MATROSKA_TRACK_ENCODING_SCOPE_CODEC_DATA, TRUE)) { + GST_WARNING_OBJECT (parse, "Decoding codec private data failed"); + ret = GST_FLOW_ERROR; + } + } + + if (context->type == 0 || context->codec_id == NULL || (ret != GST_FLOW_OK + && ret != GST_FLOW_UNEXPECTED)) { + if (ret == GST_FLOW_OK || ret == GST_FLOW_UNEXPECTED) + GST_WARNING_OBJECT (ebml, "Unknown stream/codec in track entry header"); + + parse->num_streams--; + g_ptr_array_remove_index (parse->src, parse->num_streams); + g_assert (parse->src->len == parse->num_streams); + if (context) { + gst_matroska_track_free (context); + } + + return ret; + } + + if ((context->language == NULL || *context->language == '\0') && + (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO || + context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE)) { + GST_LOG ("stream %d: language=eng (assuming default)", context->index); + context->language = g_strdup ("eng"); + } + + + /* tadaah! */ + return ret; +} + +static const GstQueryType * +gst_matroska_parse_get_src_query_types (GstPad * pad) +{ + static const GstQueryType query_types[] = { + GST_QUERY_POSITION, + GST_QUERY_DURATION, + GST_QUERY_SEEKING, + 0 + }; + + return query_types; +} + +static gboolean +gst_matroska_parse_query (GstMatroskaParse * parse, GstPad * pad, + GstQuery * query) +{ + gboolean res = FALSE; + GstMatroskaTrackContext *context = NULL; + + if (pad) { + context = gst_pad_get_element_private (pad); + } + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION: + { + GstFormat format; + + gst_query_parse_position (query, &format, NULL); + + if (format == GST_FORMAT_TIME) { + GST_OBJECT_LOCK (parse); + if (context) + gst_query_set_position (query, GST_FORMAT_TIME, context->pos); + else + gst_query_set_position (query, GST_FORMAT_TIME, + parse->segment.last_stop); + GST_OBJECT_UNLOCK (parse); + } else if (format == GST_FORMAT_DEFAULT && context + && context->default_duration) { + GST_OBJECT_LOCK (parse); + gst_query_set_position (query, GST_FORMAT_DEFAULT, + context->pos / context->default_duration); + GST_OBJECT_UNLOCK (parse); + } else { + GST_DEBUG_OBJECT (parse, + "only position query in TIME and DEFAULT format is supported"); + } + + res = TRUE; + break; + } + case GST_QUERY_DURATION: + { + GstFormat format; + + gst_query_parse_duration (query, &format, NULL); + + if (format == GST_FORMAT_TIME) { + GST_OBJECT_LOCK (parse); + gst_query_set_duration (query, GST_FORMAT_TIME, + parse->segment.duration); + GST_OBJECT_UNLOCK (parse); + } else if (format == GST_FORMAT_DEFAULT && context + && context->default_duration) { + GST_OBJECT_LOCK (parse); + gst_query_set_duration (query, GST_FORMAT_DEFAULT, + parse->segment.duration / context->default_duration); + GST_OBJECT_UNLOCK (parse); + } else { + GST_DEBUG_OBJECT (parse, + "only duration query in TIME and DEFAULT format is supported"); + } + + res = TRUE; + break; + } + + case GST_QUERY_SEEKING: + { + GstFormat fmt; + + gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL); + if (fmt == GST_FORMAT_TIME) { + gboolean seekable; + + /* assuming we'll be able to get an index ... */ + seekable = parse->seekable; + + gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, + 0, parse->segment.duration); + res = TRUE; + } + break; + } + default: + res = gst_pad_query_default (pad, query); + break; + } + + return res; +} + +static gboolean +gst_matroska_parse_element_query (GstElement * element, GstQuery * query) +{ + return gst_matroska_parse_query (GST_MATROSKA_PARSE (element), NULL, query); +} + +static gboolean +gst_matroska_parse_handle_src_query (GstPad * pad, GstQuery * query) +{ + gboolean ret; + GstMatroskaParse *parse = GST_MATROSKA_PARSE (gst_pad_get_parent (pad)); + + ret = gst_matroska_parse_query (parse, pad, query); + + gst_object_unref (parse); + + return ret; +} + +static gint +gst_matroska_index_seek_find (GstMatroskaIndex * i1, GstClockTime * time, + gpointer user_data) +{ + if (i1->time < *time) + return -1; + else if (i1->time > *time) + return 1; + else + return 0; +} + +static GstMatroskaIndex * +gst_matroskaparse_do_index_seek (GstMatroskaParse * parse, + GstMatroskaTrackContext * track, gint64 seek_pos, GArray ** _index, + gint * _entry_index) +{ + GstMatroskaIndex *entry = NULL; + GArray *index; + + if (!parse->index || !parse->index->len) + return NULL; + + /* find entry just before or at the requested position */ + if (track && track->index_table) + index = track->index_table; + else + index = parse->index; + + entry = + gst_util_array_binary_search (index->data, index->len, + sizeof (GstMatroskaIndex), + (GCompareDataFunc) gst_matroska_index_seek_find, GST_SEARCH_MODE_BEFORE, + &seek_pos, NULL); + + if (entry == NULL) + entry = &g_array_index (index, GstMatroskaIndex, 0); + + if (_index) + *_index = index; + if (_entry_index) + *_entry_index = entry - (GstMatroskaIndex *) index->data; + + return entry; +} + +/* takes ownership of taglist */ +static void +gst_matroska_parse_found_global_tag (GstMatroskaParse * parse, + GstTagList * taglist) +{ + if (parse->global_tags) { + /* nothing sent yet, add to cache */ + gst_tag_list_insert (parse->global_tags, taglist, GST_TAG_MERGE_APPEND); + gst_tag_list_free (taglist); + } else { + /* hm, already sent, no need to cache and wait anymore */ + GST_DEBUG_OBJECT (parse, "Sending late global tags %" GST_PTR_FORMAT, + taglist); + gst_element_found_tags (GST_ELEMENT (parse), taglist); + } +} + +/* returns FALSE if there are no pads to deliver event to, + * otherwise TRUE (whatever the outcome of event sending), + * takes ownership of the passed event! */ +static gboolean +gst_matroska_parse_send_event (GstMatroskaParse * parse, GstEvent * event) +{ + gboolean ret = FALSE; + + g_return_val_if_fail (event != NULL, FALSE); + + GST_DEBUG_OBJECT (parse, "Sending event of type %s to all source pads", + GST_EVENT_TYPE_NAME (event)); + + gst_pad_push_event (parse->srcpad, event); + + return ret; +} + +static gboolean +gst_matroska_parse_element_send_event (GstElement * element, GstEvent * event) +{ + GstMatroskaParse *parse = GST_MATROSKA_PARSE (element); + gboolean res; + + g_return_val_if_fail (event != NULL, FALSE); + + if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK) { + res = gst_matroska_parse_handle_seek_event (parse, NULL, event); + } else { + GST_WARNING_OBJECT (parse, "Unhandled event of type %s", + GST_EVENT_TYPE_NAME (event)); + res = FALSE; + } + gst_event_unref (event); + return res; +} + +/* determine track to seek in */ +static GstMatroskaTrackContext * +gst_matroska_parse_get_seek_track (GstMatroskaParse * parse, + GstMatroskaTrackContext * track) +{ + gint i; + + if (track && track->type == GST_MATROSKA_TRACK_TYPE_VIDEO) + return track; + + for (i = 0; i < parse->src->len; i++) { + GstMatroskaTrackContext *stream; + + stream = g_ptr_array_index (parse->src, i); + if (stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO && stream->index_table) + track = stream; + } + + return track; +} + +static void +gst_matroska_parse_reset_streams (GstMatroskaParse * parse, GstClockTime time, + gboolean full) +{ + gint i; + + GST_DEBUG_OBJECT (parse, "resetting stream state"); + + g_assert (parse->src->len == parse->num_streams); + for (i = 0; i < parse->src->len; i++) { + GstMatroskaTrackContext *context = g_ptr_array_index (parse->src, i); + context->pos = time; + context->set_discont = TRUE; + context->eos = FALSE; + context->from_time = GST_CLOCK_TIME_NONE; + if (full) + context->last_flow = GST_FLOW_OK; + if (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO) { + GstMatroskaTrackVideoContext *videocontext = + (GstMatroskaTrackVideoContext *) context; + /* parse object lock held by caller */ + videocontext->earliest_time = GST_CLOCK_TIME_NONE; + } + } +} + +/* searches for a cluster start from @pos, + * return GST_FLOW_OK and cluster position in @pos if found */ +static GstFlowReturn +gst_matroska_parse_search_cluster (GstMatroskaParse * parse, gint64 * pos) +{ + gint64 newpos = *pos; + gint64 orig_offset; + GstFlowReturn ret = GST_FLOW_OK; + const guint chunk = 64 * 1024; + GstBuffer *buf = NULL; + guint64 length; + guint32 id; + guint needed; + + orig_offset = parse->offset; + + /* read in at newpos and scan for ebml cluster id */ + while (1) { + GstByteReader reader; + gint cluster_pos; + + ret = gst_pad_pull_range (parse->sinkpad, newpos, chunk, &buf); + if (ret != GST_FLOW_OK) + break; + GST_DEBUG_OBJECT (parse, "read buffer size %d at offset %" G_GINT64_FORMAT, + GST_BUFFER_SIZE (buf), newpos); + gst_byte_reader_init_from_buffer (&reader, buf); + cluster_pos = 0; + resume: + cluster_pos = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff, + GST_MATROSKA_ID_CLUSTER, cluster_pos, + GST_BUFFER_SIZE (buf) - cluster_pos); + if (cluster_pos >= 0) { + newpos += cluster_pos; + GST_DEBUG_OBJECT (parse, + "found cluster ebml id at offset %" G_GINT64_FORMAT, newpos); + /* extra checks whether we really sync'ed to a cluster: + * - either it is the first and only cluster + * - either there is a cluster after this one + * - either cluster length is undefined + */ + /* ok if first cluster (there may not a subsequent one) */ + if (newpos == parse->first_cluster_offset) { + GST_DEBUG_OBJECT (parse, "cluster is first cluster -> OK"); + break; + } + parse->offset = newpos; + ret = + gst_matroska_parse_peek_id_length_pull (parse, &id, &length, &needed); + if (ret != GST_FLOW_OK) + goto resume; + g_assert (id == GST_MATROSKA_ID_CLUSTER); + GST_DEBUG_OBJECT (parse, "cluster size %" G_GUINT64_FORMAT ", prefix %d", + length, needed); + /* ok if undefined length or first cluster */ + if (length == G_MAXUINT64) { + GST_DEBUG_OBJECT (parse, "cluster has undefined length -> OK"); + break; + } + /* skip cluster */ + parse->offset += length + needed; + ret = + gst_matroska_parse_peek_id_length_pull (parse, &id, &length, &needed); + if (ret != GST_FLOW_OK) + goto resume; + GST_DEBUG_OBJECT (parse, "next element is %scluster", + id == GST_MATROSKA_ID_CLUSTER ? "" : "not "); + if (id == GST_MATROSKA_ID_CLUSTER) + break; + /* not ok, resume */ + goto resume; + } else { + /* partial cluster id may have been in tail of buffer */ + newpos += MAX (GST_BUFFER_SIZE (buf), 4) - 3; + gst_buffer_unref (buf); + buf = NULL; + } + } + + if (buf) { + gst_buffer_unref (buf); + buf = NULL; + } + + parse->offset = orig_offset; + *pos = newpos; + return ret; +} + + +static gboolean +gst_matroska_parse_handle_seek_event (GstMatroskaParse * parse, + GstPad * pad, GstEvent * event) +{ + GstMatroskaIndex *entry = NULL; + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + GstFormat format; + gdouble rate; + gint64 cur, stop; + GstMatroskaTrackContext *track = NULL; + GstSegment seeksegment = { 0, }; + gboolean update; + + if (pad) + track = gst_pad_get_element_private (pad); + + track = gst_matroska_parse_get_seek_track (parse, track); + + gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, + &stop_type, &stop); + + /* we can only seek on time */ + if (format != GST_FORMAT_TIME) { + GST_DEBUG_OBJECT (parse, "Can only seek on TIME"); + return FALSE; + } + + /* copy segment, we need this because we still need the old + * segment when we close the current segment. */ + memcpy (&seeksegment, &parse->segment, sizeof (GstSegment)); + + if (event) { + GST_DEBUG_OBJECT (parse, "configuring seek"); + gst_segment_set_seek (&seeksegment, rate, format, flags, + cur_type, cur, stop_type, stop, &update); + } + + GST_DEBUG_OBJECT (parse, "New segment %" GST_SEGMENT_FORMAT, &seeksegment); + + /* check sanity before we start flushing and all that */ + GST_OBJECT_LOCK (parse); + if ((entry = gst_matroskaparse_do_index_seek (parse, track, + seeksegment.last_stop, &parse->seek_index, &parse->seek_entry)) == + NULL) { + /* pull mode without index can scan later on */ + GST_DEBUG_OBJECT (parse, "No matching seek entry in index"); + GST_OBJECT_UNLOCK (parse); + return FALSE; + } + GST_DEBUG_OBJECT (parse, "Seek position looks sane"); + GST_OBJECT_UNLOCK (parse); + + /* need to seek to cluster start to pick up cluster time */ + /* upstream takes care of flushing and all that + * ... and newsegment event handling takes care of the rest */ + return perform_seek_to_offset (parse, entry->pos + parse->ebml_segment_start); +} + +/* + * Handle whether we can perform the seek event or if we have to let the chain + * function handle seeks to build the seek indexes first. + */ +static gboolean +gst_matroska_parse_handle_seek_push (GstMatroskaParse * parse, GstPad * pad, + GstEvent * event) +{ + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + GstFormat format; + gdouble rate; + gint64 cur, stop; + + gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, + &stop_type, &stop); + + /* sanity checks */ + + /* we can only seek on time */ + if (format != GST_FORMAT_TIME) { + GST_DEBUG_OBJECT (parse, "Can only seek on TIME"); + return FALSE; + } + + if (stop_type != GST_SEEK_TYPE_NONE && stop != GST_CLOCK_TIME_NONE) { + GST_DEBUG_OBJECT (parse, "Seek end-time not supported in streaming mode"); + return FALSE; + } + + if (!(flags & GST_SEEK_FLAG_FLUSH)) { + GST_DEBUG_OBJECT (parse, + "Non-flushing seek not supported in streaming mode"); + return FALSE; + } + + if (flags & GST_SEEK_FLAG_SEGMENT) { + GST_DEBUG_OBJECT (parse, "Segment seek not supported in streaming mode"); + return FALSE; + } + + /* check for having parsed index already */ + if (!parse->index_parsed) { + gboolean building_index; + guint64 offset = 0; + + if (!parse->index_offset) { + GST_DEBUG_OBJECT (parse, "no index (location); no seek in push mode"); + return FALSE; + } + + GST_OBJECT_LOCK (parse); + /* handle the seek event in the chain function */ + parse->state = GST_MATROSKA_PARSE_STATE_SEEK; + /* no more seek can be issued until state reset to _DATA */ + + /* copy the event */ + if (parse->seek_event) + gst_event_unref (parse->seek_event); + parse->seek_event = gst_event_ref (event); + + /* set the building_index flag so that only one thread can setup the + * structures for index seeking. */ + building_index = parse->building_index; + if (!building_index) { + parse->building_index = TRUE; + offset = parse->index_offset; + } + GST_OBJECT_UNLOCK (parse); + + if (!building_index) { + /* seek to the first subindex or legacy index */ + GST_INFO_OBJECT (parse, "Seeking to Cues at %" G_GUINT64_FORMAT, offset); + return perform_seek_to_offset (parse, offset); + } + + /* well, we are handling it already */ + return TRUE; + } + + /* delegate to tweaked regular seek */ + return gst_matroska_parse_handle_seek_event (parse, pad, event); +} + +static gboolean +gst_matroska_parse_handle_src_event (GstPad * pad, GstEvent * event) +{ + GstMatroskaParse *parse = GST_MATROSKA_PARSE (gst_pad_get_parent (pad)); + gboolean res = TRUE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + /* no seeking until we are (safely) ready */ + if (parse->state != GST_MATROSKA_PARSE_STATE_DATA) { + GST_DEBUG_OBJECT (parse, "not ready for seeking yet"); + return FALSE; + } + res = gst_matroska_parse_handle_seek_push (parse, pad, event); + gst_event_unref (event); + break; + + case GST_EVENT_QOS: + { + GstMatroskaTrackContext *context = gst_pad_get_element_private (pad); + if (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO) { + GstMatroskaTrackVideoContext *videocontext = + (GstMatroskaTrackVideoContext *) context; + gdouble proportion; + GstClockTimeDiff diff; + GstClockTime timestamp; + + gst_event_parse_qos (event, &proportion, &diff, ×tamp); + + GST_OBJECT_LOCK (parse); + videocontext->earliest_time = timestamp + diff; + GST_OBJECT_UNLOCK (parse); + } + res = TRUE; + gst_event_unref (event); + break; + } + + /* events we don't need to handle */ + case GST_EVENT_NAVIGATION: + gst_event_unref (event); + res = FALSE; + break; + + case GST_EVENT_LATENCY: + default: + res = gst_pad_push_event (parse->sinkpad, event); + break; + } + + gst_object_unref (parse); + + return res; +} + + +/* skip unknown or alike element */ +static GstFlowReturn +gst_matroska_parse_parse_skip (GstMatroskaParse * parse, GstEbmlRead * ebml, + const gchar * parent_name, guint id) +{ + if (id == GST_EBML_ID_VOID) { + GST_DEBUG_OBJECT (parse, "Skipping EBML Void element"); + } else if (id == GST_EBML_ID_CRC32) { + GST_DEBUG_OBJECT (parse, "Skipping EBML CRC32 element"); + } else { + GST_WARNING_OBJECT (parse, + "Unknown %s subelement 0x%x - ignoring", parent_name, id); + } + + return gst_ebml_read_skip (ebml); +} + +static GstFlowReturn +gst_matroska_parse_parse_header (GstMatroskaParse * parse, GstEbmlRead * ebml) +{ + GstFlowReturn ret; + gchar *doctype; + guint version; + guint32 id; + + /* this function is the first to be called */ + + /* default init */ + doctype = NULL; + version = 1; + + ret = gst_ebml_peek_id (ebml, &id); + if (ret != GST_FLOW_OK) + return ret; + + GST_DEBUG_OBJECT (parse, "id: %08x", id); + + if (id != GST_EBML_ID_HEADER) { + GST_ERROR_OBJECT (parse, "Failed to read header"); + goto exit; + } + + ret = gst_ebml_read_master (ebml, &id); + if (ret != GST_FLOW_OK) + return ret; + + while (gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + ret = gst_ebml_peek_id (ebml, &id); + if (ret != GST_FLOW_OK) + return ret; + + switch (id) { + /* is our read version uptodate? */ + case GST_EBML_ID_EBMLREADVERSION:{ + guint64 num; + + ret = gst_ebml_read_uint (ebml, &id, &num); + if (ret != GST_FLOW_OK) + return ret; + if (num != GST_EBML_VERSION) { + GST_ERROR_OBJECT (ebml, "Unsupported EBML version %" G_GUINT64_FORMAT, + num); + return GST_FLOW_ERROR; + } + + GST_DEBUG_OBJECT (ebml, "EbmlReadVersion: %" G_GUINT64_FORMAT, num); + break; + } + + /* we only handle 8 byte lengths at max */ + case GST_EBML_ID_EBMLMAXSIZELENGTH:{ + guint64 num; + + ret = gst_ebml_read_uint (ebml, &id, &num); + if (ret != GST_FLOW_OK) + return ret; + if (num > sizeof (guint64)) { + GST_ERROR_OBJECT (ebml, + "Unsupported EBML maximum size %" G_GUINT64_FORMAT, num); + return GST_FLOW_ERROR; + } + GST_DEBUG_OBJECT (ebml, "EbmlMaxSizeLength: %" G_GUINT64_FORMAT, num); + break; + } + + /* we handle 4 byte IDs at max */ + case GST_EBML_ID_EBMLMAXIDLENGTH:{ + guint64 num; + + ret = gst_ebml_read_uint (ebml, &id, &num); + if (ret != GST_FLOW_OK) + return ret; + if (num > sizeof (guint32)) { + GST_ERROR_OBJECT (ebml, + "Unsupported EBML maximum ID %" G_GUINT64_FORMAT, num); + return GST_FLOW_ERROR; + } + GST_DEBUG_OBJECT (ebml, "EbmlMaxIdLength: %" G_GUINT64_FORMAT, num); + break; + } + + case GST_EBML_ID_DOCTYPE:{ + gchar *text; + + ret = gst_ebml_read_ascii (ebml, &id, &text); + if (ret != GST_FLOW_OK) + return ret; + + GST_DEBUG_OBJECT (ebml, "EbmlDocType: %s", GST_STR_NULL (text)); + + if (doctype) + g_free (doctype); + doctype = text; + break; + } + + case GST_EBML_ID_DOCTYPEREADVERSION:{ + guint64 num; + + ret = gst_ebml_read_uint (ebml, &id, &num); + if (ret != GST_FLOW_OK) + return ret; + version = num; + GST_DEBUG_OBJECT (ebml, "EbmlReadVersion: %" G_GUINT64_FORMAT, num); + break; + } + + default: + ret = gst_matroska_parse_parse_skip (parse, ebml, "EBML header", id); + if (ret != GST_FLOW_OK) + return ret; + break; + + /* we ignore these two, as they don't tell us anything we care about */ + case GST_EBML_ID_EBMLVERSION: + case GST_EBML_ID_DOCTYPEVERSION: + ret = gst_ebml_read_skip (ebml); + if (ret != GST_FLOW_OK) + return ret; + break; + } + } + +exit: + + if ((doctype != NULL && !strcmp (doctype, GST_MATROSKA_DOCTYPE_MATROSKA)) || + (doctype != NULL && !strcmp (doctype, GST_MATROSKA_DOCTYPE_WEBM)) || + (doctype == NULL)) { + if (version <= 2) { + if (doctype) { + GST_INFO_OBJECT (parse, "Input is %s version %d", doctype, version); + } else { + GST_WARNING_OBJECT (parse, "Input is EBML without doctype, assuming " + "matroska (version %d)", version); + } + ret = GST_FLOW_OK; + } else { + GST_ELEMENT_ERROR (parse, STREAM, DEMUX, (NULL), + ("Parser version (2) is too old to read %s version %d", + GST_STR_NULL (doctype), version)); + ret = GST_FLOW_ERROR; + } + g_free (doctype); + } else { + GST_ELEMENT_ERROR (parse, STREAM, WRONG_TYPE, (NULL), + ("Input is not a matroska stream (doctype=%s)", doctype)); + ret = GST_FLOW_ERROR; + g_free (doctype); + } + + return ret; +} + +static GstFlowReturn +gst_matroska_parse_parse_tracks (GstMatroskaParse * parse, GstEbmlRead * ebml) +{ + GstFlowReturn ret = GST_FLOW_OK; + guint32 id; + + DEBUG_ELEMENT_START (parse, ebml, "Tracks"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "Tracks", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + /* one track within the "all-tracks" header */ + case GST_MATROSKA_ID_TRACKENTRY: + ret = gst_matroska_parse_add_stream (parse, ebml); + break; + + default: + ret = gst_matroska_parse_parse_skip (parse, ebml, "Track", id); + break; + } + } + DEBUG_ELEMENT_STOP (parse, ebml, "Tracks", ret); + + parse->tracks_parsed = TRUE; + + return ret; +} + +static GstFlowReturn +gst_matroska_parse_parse_index_cuetrack (GstMatroskaParse * parse, + GstEbmlRead * ebml, guint * nentries) +{ + guint32 id; + GstFlowReturn ret; + GstMatroskaIndex idx; + + idx.pos = (guint64) - 1; + idx.track = 0; + idx.time = GST_CLOCK_TIME_NONE; + idx.block = 1; + + DEBUG_ELEMENT_START (parse, ebml, "CueTrackPositions"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "CueTrackPositions", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + /* track number */ + case GST_MATROSKA_ID_CUETRACK: + { + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num == 0) { + idx.track = 0; + GST_WARNING_OBJECT (parse, "Invalid CueTrack 0"); + break; + } + + GST_DEBUG_OBJECT (parse, "CueTrack: %" G_GUINT64_FORMAT, num); + idx.track = num; + break; + } + + /* position in file */ + case GST_MATROSKA_ID_CUECLUSTERPOSITION: + { + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num > G_MAXINT64) { + GST_WARNING_OBJECT (parse, "CueClusterPosition %" G_GUINT64_FORMAT + " too large", num); + break; + } + + idx.pos = num; + break; + } + + /* number of block in the cluster */ + case GST_MATROSKA_ID_CUEBLOCKNUMBER: + { + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num == 0) { + GST_WARNING_OBJECT (parse, "Invalid CueBlockNumber 0"); + break; + } + + GST_DEBUG_OBJECT (parse, "CueBlockNumber: %" G_GUINT64_FORMAT, num); + idx.block = num; + + /* mild sanity check, disregard strange cases ... */ + if (idx.block > G_MAXUINT16) { + GST_DEBUG_OBJECT (parse, "... looks suspicious, ignoring"); + idx.block = 1; + } + break; + } + + default: + ret = gst_matroska_parse_parse_skip (parse, ebml, "CueTrackPositions", + id); + break; + + case GST_MATROSKA_ID_CUECODECSTATE: + case GST_MATROSKA_ID_CUEREFERENCE: + ret = gst_ebml_read_skip (ebml); + break; + } + } + + DEBUG_ELEMENT_STOP (parse, ebml, "CueTrackPositions", ret); + + if ((ret == GST_FLOW_OK || ret == GST_FLOW_UNEXPECTED) + && idx.pos != (guint64) - 1 && idx.track > 0) { + g_array_append_val (parse->index, idx); + (*nentries)++; + } else if (ret == GST_FLOW_OK || ret == GST_FLOW_UNEXPECTED) { + GST_DEBUG_OBJECT (parse, "CueTrackPositions without valid content"); + } + + return ret; +} + +static GstFlowReturn +gst_matroska_parse_parse_index_pointentry (GstMatroskaParse * parse, + GstEbmlRead * ebml) +{ + guint32 id; + GstFlowReturn ret; + GstClockTime time = GST_CLOCK_TIME_NONE; + guint nentries = 0; + + DEBUG_ELEMENT_START (parse, ebml, "CuePoint"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "CuePoint", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + /* one single index entry ('point') */ + case GST_MATROSKA_ID_CUETIME: + { + if ((ret = gst_ebml_read_uint (ebml, &id, &time)) != GST_FLOW_OK) + break; + + GST_DEBUG_OBJECT (parse, "CueTime: %" G_GUINT64_FORMAT, time); + time = time * parse->time_scale; + break; + } + + /* position in the file + track to which it belongs */ + case GST_MATROSKA_ID_CUETRACKPOSITIONS: + { + if ((ret = + gst_matroska_parse_parse_index_cuetrack (parse, ebml, + &nentries)) != GST_FLOW_OK) + break; + break; + } + + default: + ret = gst_matroska_parse_parse_skip (parse, ebml, "CuePoint", id); + break; + } + } + + DEBUG_ELEMENT_STOP (parse, ebml, "CuePoint", ret); + + if (nentries > 0) { + if (time == GST_CLOCK_TIME_NONE) { + GST_WARNING_OBJECT (parse, "CuePoint without valid time"); + g_array_remove_range (parse->index, parse->index->len - nentries, + nentries); + } else { + gint i; + + for (i = parse->index->len - nentries; i < parse->index->len; i++) { + GstMatroskaIndex *idx = + &g_array_index (parse->index, GstMatroskaIndex, i); + + idx->time = time; + GST_DEBUG_OBJECT (parse, "Index entry: pos=%" G_GUINT64_FORMAT + ", time=%" GST_TIME_FORMAT ", track=%u, block=%u", idx->pos, + GST_TIME_ARGS (idx->time), (guint) idx->track, (guint) idx->block); + } + } + } else { + GST_DEBUG_OBJECT (parse, "Empty CuePoint"); + } + + return ret; +} + +static gint +gst_matroska_index_compare (GstMatroskaIndex * i1, GstMatroskaIndex * i2) +{ + if (i1->time < i2->time) + return -1; + else if (i1->time > i2->time) + return 1; + else if (i1->block < i2->block) + return -1; + else if (i1->block > i2->block) + return 1; + else + return 0; +} + +static GstFlowReturn +gst_matroska_parse_parse_index (GstMatroskaParse * parse, GstEbmlRead * ebml) +{ + guint32 id; + GstFlowReturn ret = GST_FLOW_OK; + guint i; + + if (parse->index) + g_array_free (parse->index, TRUE); + parse->index = + g_array_sized_new (FALSE, FALSE, sizeof (GstMatroskaIndex), 128); + + DEBUG_ELEMENT_START (parse, ebml, "Cues"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "Cues", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + /* one single index entry ('point') */ + case GST_MATROSKA_ID_POINTENTRY: + ret = gst_matroska_parse_parse_index_pointentry (parse, ebml); + break; + + default: + ret = gst_matroska_parse_parse_skip (parse, ebml, "Cues", id); + break; + } + } + DEBUG_ELEMENT_STOP (parse, ebml, "Cues", ret); + + /* Sort index by time, smallest time first, for easier searching */ + g_array_sort (parse->index, (GCompareFunc) gst_matroska_index_compare); + + /* Now sort the track specific index entries into their own arrays */ + for (i = 0; i < parse->index->len; i++) { + GstMatroskaIndex *idx = &g_array_index (parse->index, GstMatroskaIndex, i); + gint track_num; + GstMatroskaTrackContext *ctx; + + if (parse->element_index) { + gint writer_id; + + if (idx->track != 0 && + (track_num = + gst_matroska_parse_stream_from_num (parse, idx->track)) != -1) { + ctx = g_ptr_array_index (parse->src, track_num); + + if (ctx->index_writer_id == -1) + gst_index_get_writer_id (parse->element_index, GST_OBJECT (ctx->pad), + &ctx->index_writer_id); + writer_id = ctx->index_writer_id; + } else { + if (parse->element_index_writer_id == -1) + gst_index_get_writer_id (parse->element_index, GST_OBJECT (parse), + &parse->element_index_writer_id); + writer_id = parse->element_index_writer_id; + } + + GST_LOG_OBJECT (parse, "adding association %" GST_TIME_FORMAT "-> %" + G_GUINT64_FORMAT " for writer id %d", GST_TIME_ARGS (idx->time), + idx->pos, writer_id); + gst_index_add_association (parse->element_index, writer_id, + GST_ASSOCIATION_FLAG_KEY_UNIT, GST_FORMAT_TIME, idx->time, + GST_FORMAT_BYTES, idx->pos + parse->ebml_segment_start, NULL); + } + + if (idx->track == 0) + continue; + + track_num = gst_matroska_parse_stream_from_num (parse, idx->track); + if (track_num == -1) + continue; + + ctx = g_ptr_array_index (parse->src, track_num); + + if (ctx->index_table == NULL) + ctx->index_table = + g_array_sized_new (FALSE, FALSE, sizeof (GstMatroskaIndex), 128); + + g_array_append_vals (ctx->index_table, idx, 1); + } + + parse->index_parsed = TRUE; + + /* sanity check; empty index normalizes to no index */ + if (parse->index->len == 0) { + g_array_free (parse->index, TRUE); + parse->index = NULL; + } + + return ret; +} + +static GstFlowReturn +gst_matroska_parse_parse_info (GstMatroskaParse * parse, GstEbmlRead * ebml) +{ + GstFlowReturn ret = GST_FLOW_OK; + guint32 id; + + DEBUG_ELEMENT_START (parse, ebml, "SegmentInfo"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "SegmentInfo", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + /* cluster timecode */ + case GST_MATROSKA_ID_TIMECODESCALE:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + + GST_DEBUG_OBJECT (parse, "TimeCodeScale: %" G_GUINT64_FORMAT, num); + parse->time_scale = num; + break; + } + + case GST_MATROSKA_ID_DURATION:{ + gdouble num; + GstClockTime dur; + + if ((ret = gst_ebml_read_float (ebml, &id, &num)) != GST_FLOW_OK) + break; + + if (num <= 0.0) { + GST_WARNING_OBJECT (parse, "Invalid duration %lf", num); + break; + } + + GST_DEBUG_OBJECT (parse, "Duration: %lf", num); + + dur = gst_gdouble_to_guint64 (num * + gst_guint64_to_gdouble (parse->time_scale)); + if (GST_CLOCK_TIME_IS_VALID (dur) && dur <= G_MAXINT64) + gst_segment_set_duration (&parse->segment, GST_FORMAT_TIME, dur); + break; + } + + case GST_MATROSKA_ID_WRITINGAPP:{ + gchar *text; + + if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK) + break; + + GST_DEBUG_OBJECT (parse, "WritingApp: %s", GST_STR_NULL (text)); + parse->writing_app = text; + break; + } + + case GST_MATROSKA_ID_MUXINGAPP:{ + gchar *text; + + if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK) + break; + + GST_DEBUG_OBJECT (parse, "MuxingApp: %s", GST_STR_NULL (text)); + parse->muxing_app = text; + break; + } + + case GST_MATROSKA_ID_DATEUTC:{ + gint64 time; + + if ((ret = gst_ebml_read_date (ebml, &id, &time)) != GST_FLOW_OK) + break; + + GST_DEBUG_OBJECT (parse, "DateUTC: %" G_GINT64_FORMAT, time); + parse->created = time; + break; + } + + case GST_MATROSKA_ID_TITLE:{ + gchar *text; + GstTagList *taglist; + + if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK) + break; + + GST_DEBUG_OBJECT (parse, "Title: %s", GST_STR_NULL (text)); + taglist = gst_tag_list_new (); + gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_TITLE, text, + NULL); + gst_matroska_parse_found_global_tag (parse, taglist); + g_free (text); + break; + } + + default: + ret = gst_matroska_parse_parse_skip (parse, ebml, "SegmentInfo", id); + break; + + /* fall through */ + case GST_MATROSKA_ID_SEGMENTUID: + case GST_MATROSKA_ID_SEGMENTFILENAME: + case GST_MATROSKA_ID_PREVUID: + case GST_MATROSKA_ID_PREVFILENAME: + case GST_MATROSKA_ID_NEXTUID: + case GST_MATROSKA_ID_NEXTFILENAME: + case GST_MATROSKA_ID_SEGMENTFAMILY: + case GST_MATROSKA_ID_CHAPTERTRANSLATE: + ret = gst_ebml_read_skip (ebml); + break; + } + } + + DEBUG_ELEMENT_STOP (parse, ebml, "SegmentInfo", ret); + + parse->segmentinfo_parsed = TRUE; + + return ret; +} + +static GstFlowReturn +gst_matroska_parse_parse_metadata_id_simple_tag (GstMatroskaParse * parse, + GstEbmlRead * ebml, GstTagList ** p_taglist) +{ + /* FIXME: check if there are more useful mappings */ + struct + { + const gchar *matroska_tagname; + const gchar *gstreamer_tagname; + } + tag_conv[] = { + { + GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, { + GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, { + GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, { + GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, { + GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, { + GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, { + GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, { + GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, { + GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, { + GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, { + GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, { + GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, { + GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, { + GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, { + GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE} + }; + GstFlowReturn ret; + guint32 id; + gchar *value = NULL; + gchar *tag = NULL; + + DEBUG_ELEMENT_START (parse, ebml, "SimpleTag"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "SimpleTag", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + /* read all sub-entries */ + + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + case GST_MATROSKA_ID_TAGNAME: + g_free (tag); + tag = NULL; + ret = gst_ebml_read_ascii (ebml, &id, &tag); + GST_DEBUG_OBJECT (parse, "TagName: %s", GST_STR_NULL (tag)); + break; + + case GST_MATROSKA_ID_TAGSTRING: + g_free (value); + value = NULL; + ret = gst_ebml_read_utf8 (ebml, &id, &value); + GST_DEBUG_OBJECT (parse, "TagString: %s", GST_STR_NULL (value)); + break; + + default: + ret = gst_matroska_parse_parse_skip (parse, ebml, "SimpleTag", id); + break; + /* fall-through */ + + case GST_MATROSKA_ID_TAGLANGUAGE: + case GST_MATROSKA_ID_TAGDEFAULT: + case GST_MATROSKA_ID_TAGBINARY: + ret = gst_ebml_read_skip (ebml); + break; + } + } + + DEBUG_ELEMENT_STOP (parse, ebml, "SimpleTag", ret); + + if (tag && value) { + guint i; + + for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) { + const gchar *tagname_gst = tag_conv[i].gstreamer_tagname; + + const gchar *tagname_mkv = tag_conv[i].matroska_tagname; + + if (strcmp (tagname_mkv, tag) == 0) { + GValue dest = { 0, }; + GType dest_type = gst_tag_get_type (tagname_gst); + + /* Ensure that any date string is complete */ + if (dest_type == GST_TYPE_DATE) { + guint year = 1901, month = 1, day = 1; + + /* Dates can be yyyy-MM-dd, yyyy-MM or yyyy, but we need + * the first type */ + if (sscanf (value, "%04u-%02u-%02u", &year, &month, &day) != 0) { + g_free (value); + value = g_strdup_printf ("%04u-%02u-%02u", year, month, day); + } + } + + g_value_init (&dest, dest_type); + if (gst_value_deserialize (&dest, value)) { + gst_tag_list_add_values (*p_taglist, GST_TAG_MERGE_APPEND, + tagname_gst, &dest, NULL); + } else { + GST_WARNING_OBJECT (parse, "Can't transform tag '%s' with " + "value '%s' to target type '%s'", tag, value, + g_type_name (dest_type)); + } + g_value_unset (&dest); + break; + } + } + } + + g_free (tag); + g_free (value); + + return ret; +} + +static GstFlowReturn +gst_matroska_parse_parse_metadata_id_tag (GstMatroskaParse * parse, + GstEbmlRead * ebml, GstTagList ** p_taglist) +{ + guint32 id; + GstFlowReturn ret; + + DEBUG_ELEMENT_START (parse, ebml, "Tag"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "Tag", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + /* read all sub-entries */ + + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + case GST_MATROSKA_ID_SIMPLETAG: + ret = gst_matroska_parse_parse_metadata_id_simple_tag (parse, ebml, + p_taglist); + break; + + default: + ret = gst_matroska_parse_parse_skip (parse, ebml, "Tag", id); + break; + } + } + + DEBUG_ELEMENT_STOP (parse, ebml, "Tag", ret); + + return ret; +} + +static GstFlowReturn +gst_matroska_parse_parse_metadata (GstMatroskaParse * parse, GstEbmlRead * ebml) +{ + GstTagList *taglist; + GstFlowReturn ret = GST_FLOW_OK; + guint32 id; + GList *l; + guint64 curpos; + + curpos = gst_ebml_read_get_pos (ebml); + + /* Make sure we don't parse a tags element twice and + * post it's tags twice */ + curpos = gst_ebml_read_get_pos (ebml); + for (l = parse->tags_parsed; l; l = l->next) { + guint64 *pos = l->data; + + if (*pos == curpos) { + GST_DEBUG_OBJECT (parse, "Skipping already parsed Tags at offset %" + G_GUINT64_FORMAT, curpos); + return GST_FLOW_OK; + } + } + + parse->tags_parsed = + g_list_prepend (parse->tags_parsed, g_slice_new (guint64)); + *((guint64 *) parse->tags_parsed->data) = curpos; + /* fall-through */ + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "Tags", ret); + return ret; + } + + taglist = gst_tag_list_new (); + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + case GST_MATROSKA_ID_TAG: + ret = gst_matroska_parse_parse_metadata_id_tag (parse, ebml, &taglist); + break; + + default: + ret = gst_matroska_parse_parse_skip (parse, ebml, "Tags", id); + break; + /* FIXME: Use to limit the tags to specific pads */ + case GST_MATROSKA_ID_TARGETS: + ret = gst_ebml_read_skip (ebml); + break; + } + } + + DEBUG_ELEMENT_STOP (parse, ebml, "Tags", ret); + + gst_matroska_parse_found_global_tag (parse, taglist); + + return ret; +} + +static GstFlowReturn +gst_matroska_parse_parse_attached_file (GstMatroskaParse * parse, + GstEbmlRead * ebml, GstTagList * taglist) +{ + guint32 id; + GstFlowReturn ret; + gchar *description = NULL; + gchar *filename = NULL; + gchar *mimetype = NULL; + guint8 *data = NULL; + guint64 datalen = 0; + + DEBUG_ELEMENT_START (parse, ebml, "AttachedFile"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "AttachedFile", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + /* read all sub-entries */ + + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + case GST_MATROSKA_ID_FILEDESCRIPTION: + if (description) { + GST_WARNING_OBJECT (parse, "FileDescription can only appear once"); + break; + } + + ret = gst_ebml_read_utf8 (ebml, &id, &description); + GST_DEBUG_OBJECT (parse, "FileDescription: %s", + GST_STR_NULL (description)); + break; + case GST_MATROSKA_ID_FILENAME: + if (filename) { + GST_WARNING_OBJECT (parse, "FileName can only appear once"); + break; + } + + ret = gst_ebml_read_utf8 (ebml, &id, &filename); + + GST_DEBUG_OBJECT (parse, "FileName: %s", GST_STR_NULL (filename)); + break; + case GST_MATROSKA_ID_FILEMIMETYPE: + if (mimetype) { + GST_WARNING_OBJECT (parse, "FileMimeType can only appear once"); + break; + } + + ret = gst_ebml_read_ascii (ebml, &id, &mimetype); + GST_DEBUG_OBJECT (parse, "FileMimeType: %s", GST_STR_NULL (mimetype)); + break; + case GST_MATROSKA_ID_FILEDATA: + if (data) { + GST_WARNING_OBJECT (parse, "FileData can only appear once"); + break; + } + + ret = gst_ebml_read_binary (ebml, &id, &data, &datalen); + GST_DEBUG_OBJECT (parse, "FileData of size %" G_GUINT64_FORMAT, + datalen); + break; + + default: + ret = gst_matroska_parse_parse_skip (parse, ebml, "AttachedFile", id); + break; + case GST_MATROSKA_ID_FILEUID: + ret = gst_ebml_read_skip (ebml); + break; + } + } + + DEBUG_ELEMENT_STOP (parse, ebml, "AttachedFile", ret); + + if (filename && mimetype && data && datalen > 0) { + GstTagImageType image_type = GST_TAG_IMAGE_TYPE_NONE; + GstBuffer *tagbuffer = NULL; + GstCaps *caps; + gchar *filename_lc = g_utf8_strdown (filename, -1); + + GST_DEBUG_OBJECT (parse, "Creating tag for attachment with filename '%s', " + "mimetype '%s', description '%s', size %" G_GUINT64_FORMAT, filename, + mimetype, GST_STR_NULL (description), datalen); + + /* TODO: better heuristics for different image types */ + if (strstr (filename_lc, "cover")) { + if (strstr (filename_lc, "back")) + image_type = GST_TAG_IMAGE_TYPE_BACK_COVER; + else + image_type = GST_TAG_IMAGE_TYPE_FRONT_COVER; + } else if (g_str_has_prefix (mimetype, "image/") || + g_str_has_suffix (filename_lc, "png") || + g_str_has_suffix (filename_lc, "jpg") || + g_str_has_suffix (filename_lc, "jpeg") || + g_str_has_suffix (filename_lc, "gif") || + g_str_has_suffix (filename_lc, "bmp")) { + image_type = GST_TAG_IMAGE_TYPE_UNDEFINED; + } + g_free (filename_lc); + + /* First try to create an image tag buffer from this */ + if (image_type != GST_TAG_IMAGE_TYPE_NONE) { + tagbuffer = + gst_tag_image_data_to_image_buffer (data, datalen, image_type); + + if (!tagbuffer) + image_type = GST_TAG_IMAGE_TYPE_NONE; + } + + /* if this failed create an attachment buffer */ + if (!tagbuffer) { + tagbuffer = gst_buffer_new_and_alloc (datalen); + + memcpy (GST_BUFFER_DATA (tagbuffer), data, datalen); + GST_BUFFER_SIZE (tagbuffer) = datalen; + + caps = gst_type_find_helper_for_buffer (NULL, tagbuffer, NULL); + if (caps == NULL) + caps = gst_caps_new_simple (mimetype, NULL); + gst_buffer_set_caps (tagbuffer, caps); + gst_caps_unref (caps); + } + + /* Set filename and description on the caps */ + caps = GST_BUFFER_CAPS (tagbuffer); + gst_caps_set_simple (caps, "filename", G_TYPE_STRING, filename, NULL); + if (description) + gst_caps_set_simple (caps, "description", G_TYPE_STRING, description, + NULL); + + GST_DEBUG_OBJECT (parse, + "Created attachment buffer with caps: %" GST_PTR_FORMAT, caps); + + /* and append to the tag list */ + if (image_type != GST_TAG_IMAGE_TYPE_NONE) + gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_IMAGE, tagbuffer, + NULL); + else + gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_ATTACHMENT, + tagbuffer, NULL); + } + + g_free (filename); + g_free (mimetype); + g_free (data); + g_free (description); + + return ret; +} + +static GstFlowReturn +gst_matroska_parse_parse_attachments (GstMatroskaParse * parse, + GstEbmlRead * ebml) +{ + guint32 id; + GstFlowReturn ret = GST_FLOW_OK; + GstTagList *taglist; + + DEBUG_ELEMENT_START (parse, ebml, "Attachments"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "Attachments", ret); + return ret; + } + + taglist = gst_tag_list_new (); + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + case GST_MATROSKA_ID_ATTACHEDFILE: + ret = gst_matroska_parse_parse_attached_file (parse, ebml, taglist); + break; + + default: + ret = gst_matroska_parse_parse_skip (parse, ebml, "Attachments", id); + break; + } + } + DEBUG_ELEMENT_STOP (parse, ebml, "Attachments", ret); + + if (gst_structure_n_fields (GST_STRUCTURE (taglist)) > 0) { + GST_DEBUG_OBJECT (parse, "Storing attachment tags"); + gst_matroska_parse_found_global_tag (parse, taglist); + } else { + GST_DEBUG_OBJECT (parse, "No valid attachments found"); + gst_tag_list_free (taglist); + } + + parse->attachments_parsed = TRUE; + + return ret; +} + +static GstFlowReturn +gst_matroska_parse_parse_chapters (GstMatroskaParse * parse, GstEbmlRead * ebml) +{ + guint32 id; + GstFlowReturn ret = GST_FLOW_OK; + + GST_WARNING_OBJECT (parse, "Parsing of chapters not implemented yet"); + + /* TODO: implement parsing of chapters */ + + DEBUG_ELEMENT_START (parse, ebml, "Chapters"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "Chapters", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + default: + ret = gst_ebml_read_skip (ebml); + break; + } + } + + DEBUG_ELEMENT_STOP (parse, ebml, "Chapters", ret); + return ret; +} + +/* + * Read signed/unsigned "EBML" numbers. + * Return: number of bytes processed. + */ + +static gint +gst_matroska_ebmlnum_uint (guint8 * data, guint size, guint64 * num) +{ + gint len_mask = 0x80, read = 1, n = 1, num_ffs = 0; + guint64 total; + + if (size <= 0) { + return -1; + } + + total = data[0]; + while (read <= 8 && !(total & len_mask)) { + read++; + len_mask >>= 1; + } + if (read > 8) + return -1; + + if ((total &= (len_mask - 1)) == len_mask - 1) + num_ffs++; + if (size < read) + return -1; + while (n < read) { + if (data[n] == 0xff) + num_ffs++; + total = (total << 8) | data[n]; + n++; + } + + if (read == num_ffs && total != 0) + *num = G_MAXUINT64; + else + *num = total; + + return read; +} + +static gint +gst_matroska_ebmlnum_sint (guint8 * data, guint size, gint64 * num) +{ + guint64 unum; + gint res; + + /* read as unsigned number first */ + if ((res = gst_matroska_ebmlnum_uint (data, size, &unum)) < 0) + return -1; + + /* make signed */ + if (unum == G_MAXUINT64) + *num = G_MAXINT64; + else + *num = unum - ((1 << ((7 * res) - 1)) - 1); + + return res; +} + +static GstFlowReturn +gst_matroska_parse_parse_blockgroup_or_simpleblock (GstMatroskaParse * parse, + GstEbmlRead * ebml, guint64 cluster_time, guint64 cluster_offset, + gboolean is_simpleblock) +{ + GstMatroskaTrackContext *stream = NULL; + GstFlowReturn ret = GST_FLOW_OK; + gboolean readblock = FALSE; + guint32 id; + guint64 block_duration = 0; + GstBuffer *buf = NULL; + gint stream_num = -1, n, laces = 0; + guint size = 0; + gint *lace_size = NULL; + gint64 time = 0; + gint flags = 0; + gint64 referenceblock = 0; + gint64 offset; + + offset = gst_ebml_read_get_offset (ebml); + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if (!is_simpleblock) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) { + goto data_error; + } + } else { + id = GST_MATROSKA_ID_SIMPLEBLOCK; + } + + switch (id) { + /* one block inside the group. Note, block parsing is one + * of the harder things, so this code is a bit complicated. + * See http://www.matroska.org/ for documentation. */ + case GST_MATROSKA_ID_SIMPLEBLOCK: + case GST_MATROSKA_ID_BLOCK: + { + guint64 num; + guint8 *data; + + if (buf) { + gst_buffer_unref (buf); + buf = NULL; + } + if ((ret = gst_ebml_read_buffer (ebml, &id, &buf)) != GST_FLOW_OK) + break; + + data = GST_BUFFER_DATA (buf); + size = GST_BUFFER_SIZE (buf); + + /* first byte(s): blocknum */ + if ((n = gst_matroska_ebmlnum_uint (data, size, &num)) < 0) + goto data_error; + data += n; + size -= n; + + /* fetch stream from num */ + stream_num = gst_matroska_parse_stream_from_num (parse, num); + if (G_UNLIKELY (size < 3)) { + GST_WARNING_OBJECT (parse, "Invalid size %u", size); + /* non-fatal, try next block(group) */ + ret = GST_FLOW_OK; + goto done; + } else if (G_UNLIKELY (stream_num < 0 || + stream_num >= parse->num_streams)) { + /* let's not give up on a stray invalid track number */ + GST_WARNING_OBJECT (parse, + "Invalid stream %d for track number %" G_GUINT64_FORMAT + "; ignoring block", stream_num, num); + goto done; + } + + stream = g_ptr_array_index (parse->src, stream_num); + + /* time (relative to cluster time) */ + time = ((gint16) GST_READ_UINT16_BE (data)); + data += 2; + size -= 2; + flags = GST_READ_UINT8 (data); + data += 1; + size -= 1; + + GST_LOG_OBJECT (parse, "time %" G_GUINT64_FORMAT ", flags %d", time, + flags); + + switch ((flags & 0x06) >> 1) { + case 0x0: /* no lacing */ + laces = 1; + lace_size = g_new (gint, 1); + lace_size[0] = size; + break; + + case 0x1: /* xiph lacing */ + case 0x2: /* fixed-size lacing */ + case 0x3: /* EBML lacing */ + if (size == 0) + goto invalid_lacing; + laces = GST_READ_UINT8 (data) + 1; + data += 1; + size -= 1; + lace_size = g_new0 (gint, laces); + + switch ((flags & 0x06) >> 1) { + case 0x1: /* xiph lacing */ { + guint temp, total = 0; + + for (n = 0; ret == GST_FLOW_OK && n < laces - 1; n++) { + while (1) { + if (size == 0) + goto invalid_lacing; + temp = GST_READ_UINT8 (data); + lace_size[n] += temp; + data += 1; + size -= 1; + if (temp != 0xff) + break; + } + total += lace_size[n]; + } + lace_size[n] = size - total; + break; + } + + case 0x2: /* fixed-size lacing */ + for (n = 0; n < laces; n++) + lace_size[n] = size / laces; + break; + + case 0x3: /* EBML lacing */ { + guint total; + + if ((n = gst_matroska_ebmlnum_uint (data, size, &num)) < 0) + goto data_error; + data += n; + size -= n; + total = lace_size[0] = num; + for (n = 1; ret == GST_FLOW_OK && n < laces - 1; n++) { + gint64 snum; + gint r; + + if ((r = gst_matroska_ebmlnum_sint (data, size, &snum)) < 0) + goto data_error; + data += r; + size -= r; + lace_size[n] = lace_size[n - 1] + snum; + total += lace_size[n]; + } + if (n < laces) + lace_size[n] = size - total; + break; + } + } + break; + } + + if (ret != GST_FLOW_OK) + break; + + readblock = TRUE; + break; + } + + case GST_MATROSKA_ID_BLOCKDURATION:{ + ret = gst_ebml_read_uint (ebml, &id, &block_duration); + GST_DEBUG_OBJECT (parse, "BlockDuration: %" G_GUINT64_FORMAT, + block_duration); + break; + } + + case GST_MATROSKA_ID_REFERENCEBLOCK:{ + ret = gst_ebml_read_sint (ebml, &id, &referenceblock); + GST_DEBUG_OBJECT (parse, "ReferenceBlock: %" G_GINT64_FORMAT, + referenceblock); + break; + } + + case GST_MATROSKA_ID_CODECSTATE:{ + guint8 *data; + guint64 data_len = 0; + + if ((ret = + gst_ebml_read_binary (ebml, &id, &data, + &data_len)) != GST_FLOW_OK) + break; + + if (G_UNLIKELY (stream == NULL)) { + GST_WARNING_OBJECT (parse, + "Unexpected CodecState subelement - ignoring"); + break; + } + + g_free (stream->codec_state); + stream->codec_state = data; + stream->codec_state_size = data_len; + + break; + } + + default: + ret = gst_matroska_parse_parse_skip (parse, ebml, "BlockGroup", id); + break; + + case GST_MATROSKA_ID_BLOCKVIRTUAL: + case GST_MATROSKA_ID_BLOCKADDITIONS: + case GST_MATROSKA_ID_REFERENCEPRIORITY: + case GST_MATROSKA_ID_REFERENCEVIRTUAL: + case GST_MATROSKA_ID_SLICES: + GST_DEBUG_OBJECT (parse, + "Skipping BlockGroup subelement 0x%x - ignoring", id); + ret = gst_ebml_read_skip (ebml); + break; + } + + if (is_simpleblock) + break; + } + + /* reading a number or so could have failed */ + if (ret != GST_FLOW_OK) + goto data_error; + + if (ret == GST_FLOW_OK && readblock) { + guint64 duration = 0; + gint64 lace_time = 0; + gboolean delta_unit; + + stream = g_ptr_array_index (parse->src, stream_num); + + if (cluster_time != GST_CLOCK_TIME_NONE) { + /* FIXME: What to do with negative timestamps? Give timestamp 0 or -1? + * Drop unless the lace contains timestamp 0? */ + if (time < 0 && (-time) > cluster_time) { + lace_time = 0; + } else { + if (stream->timecodescale == 1.0) + lace_time = (cluster_time + time) * parse->time_scale; + else + lace_time = + gst_util_guint64_to_gdouble ((cluster_time + time) * + parse->time_scale) * stream->timecodescale; + } + } else { + lace_time = GST_CLOCK_TIME_NONE; + } + + if (lace_time != GST_CLOCK_TIME_NONE) { + parse->last_timestamp = lace_time; + } + /* need to refresh segment info ASAP */ + if (GST_CLOCK_TIME_IS_VALID (lace_time) && parse->need_newsegment) { + GST_DEBUG_OBJECT (parse, + "generating segment starting at %" GST_TIME_FORMAT, + GST_TIME_ARGS (lace_time)); + /* pretend we seeked here */ + gst_segment_set_seek (&parse->segment, parse->segment.rate, + GST_FORMAT_TIME, 0, GST_SEEK_TYPE_SET, lace_time, + GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE, NULL); + /* now convey our segment notion downstream */ + gst_matroska_parse_send_event (parse, gst_event_new_new_segment (FALSE, + parse->segment.rate, parse->segment.format, parse->segment.start, + parse->segment.stop, parse->segment.start)); + parse->need_newsegment = FALSE; + } + + if (block_duration) { + if (stream->timecodescale == 1.0) + duration = gst_util_uint64_scale (block_duration, parse->time_scale, 1); + else + duration = + gst_util_gdouble_to_guint64 (gst_util_guint64_to_gdouble + (gst_util_uint64_scale (block_duration, parse->time_scale, + 1)) * stream->timecodescale); + } else if (stream->default_duration) { + duration = stream->default_duration * laces; + } + /* else duration is diff between timecode of this and next block */ + + /* For SimpleBlock, look at the keyframe bit in flags. Otherwise, + a ReferenceBlock implies that this is not a keyframe. In either + case, it only makes sense for video streams. */ + delta_unit = stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO && + ((is_simpleblock && !(flags & 0x80)) || referenceblock); + + if (delta_unit && stream->set_discont) { + /* When doing seeks or such, we need to restart on key frames or + * decoders might choke. */ + GST_DEBUG_OBJECT (parse, "skipping delta unit"); + goto done; + } + + for (n = 0; n < laces; n++) { + if (G_UNLIKELY (lace_size[n] > size)) { + GST_WARNING_OBJECT (parse, "Invalid lace size"); + break; + } + + /* QoS for video track with an index. the assumption is that + index entries point to keyframes, but if that is not true we + will instad skip until the next keyframe. */ + if (GST_CLOCK_TIME_IS_VALID (lace_time) && + stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO && + stream->index_table && parse->segment.rate > 0.0) { + GstMatroskaTrackVideoContext *videocontext = + (GstMatroskaTrackVideoContext *) stream; + GstClockTime earliest_time; + GstClockTime earliest_stream_time; + + GST_OBJECT_LOCK (parse); + earliest_time = videocontext->earliest_time; + GST_OBJECT_UNLOCK (parse); + earliest_stream_time = gst_segment_to_position (&parse->segment, + GST_FORMAT_TIME, earliest_time); + + if (GST_CLOCK_TIME_IS_VALID (lace_time) && + GST_CLOCK_TIME_IS_VALID (earliest_stream_time) && + lace_time <= earliest_stream_time) { + /* find index entry (keyframe) <= earliest_stream_time */ + GstMatroskaIndex *entry = + gst_util_array_binary_search (stream->index_table->data, + stream->index_table->len, sizeof (GstMatroskaIndex), + (GCompareDataFunc) gst_matroska_index_seek_find, + GST_SEARCH_MODE_BEFORE, &earliest_stream_time, NULL); + + /* if that entry (keyframe) is after the current the current + buffer, we can skip pushing (and thus decoding) all + buffers until that keyframe. */ + if (entry && GST_CLOCK_TIME_IS_VALID (entry->time) && + entry->time > lace_time) { + GST_LOG_OBJECT (parse, "Skipping lace before late keyframe"); + stream->set_discont = TRUE; + goto next_lace; + } + } + } +#if 0 + sub = gst_buffer_create_sub (buf, + GST_BUFFER_SIZE (buf) - size, lace_size[n]); + GST_DEBUG_OBJECT (parse, "created subbuffer %p", sub); + + if (delta_unit) + GST_BUFFER_FLAG_SET (sub, GST_BUFFER_FLAG_DELTA_UNIT); + else + GST_BUFFER_FLAG_UNSET (sub, GST_BUFFER_FLAG_DELTA_UNIT); + + if (stream->encodings != NULL && stream->encodings->len > 0) + sub = gst_matroska_decode_buffer (stream, sub); + + if (sub == NULL) { + GST_WARNING_OBJECT (parse, "Decoding buffer failed"); + goto next_lace; + } + + GST_BUFFER_TIMESTAMP (sub) = lace_time; + + if (GST_CLOCK_TIME_IS_VALID (lace_time)) { + GstClockTime last_stop_end; + + /* Check if this stream is after segment stop */ + if (GST_CLOCK_TIME_IS_VALID (parse->segment.stop) && + lace_time >= parse->segment.stop) { + GST_DEBUG_OBJECT (parse, + "Stream %d after segment stop %" GST_TIME_FORMAT, stream->index, + GST_TIME_ARGS (parse->segment.stop)); + gst_buffer_unref (sub); + goto eos; + } + if (offset >= stream->to_offset) { + GST_DEBUG_OBJECT (parse, "Stream %d after playback section", + stream->index); + gst_buffer_unref (sub); + goto eos; + } + + /* handle gaps, e.g. non-zero start-time, or an cue index entry + * that landed us with timestamps not quite intended */ + if (GST_CLOCK_TIME_IS_VALID (parse->segment.last_stop) && + parse->segment.rate > 0.0) { + GstClockTimeDiff diff; + + /* only send newsegments with increasing start times, + * otherwise if these go back and forth downstream (sinks) increase + * accumulated time and running_time */ + diff = GST_CLOCK_DIFF (parse->segment.last_stop, lace_time); + if (diff > 2 * GST_SECOND && lace_time > parse->segment.start && + (!GST_CLOCK_TIME_IS_VALID (parse->segment.stop) || + lace_time < parse->segment.stop)) { + GST_DEBUG_OBJECT (parse, + "Gap of %" G_GINT64_FORMAT " ns detected in" + "stream %d (%" GST_TIME_FORMAT " -> %" GST_TIME_FORMAT "). " + "Sending updated NEWSEGMENT events", diff, + stream->index, GST_TIME_ARGS (stream->pos), + GST_TIME_ARGS (lace_time)); + /* send newsegment events such that the gap is not accounted in + * accum time, hence running_time */ + /* close ahead of gap */ + gst_matroska_parse_send_event (parse, + gst_event_new_new_segment (TRUE, parse->segment.rate, + parse->segment.format, parse->segment.last_stop, + parse->segment.last_stop, parse->segment.last_stop)); + /* skip gap */ + gst_matroska_parse_send_event (parse, + gst_event_new_new_segment (FALSE, parse->segment.rate, + parse->segment.format, lace_time, parse->segment.stop, + lace_time)); + /* align segment view with downstream, + * prevents double-counting accum when closing segment */ + gst_segment_set_newsegment (&parse->segment, FALSE, + parse->segment.rate, parse->segment.format, lace_time, + parse->segment.stop, lace_time); + parse->segment.last_stop = lace_time; + } + } + + if (!GST_CLOCK_TIME_IS_VALID (parse->segment.last_stop) + || parse->segment.last_stop < lace_time) { + parse->segment.last_stop = lace_time; + } + + last_stop_end = lace_time; + if (duration) { + GST_BUFFER_DURATION (sub) = duration / laces; + last_stop_end += GST_BUFFER_DURATION (sub); + } + + if (!GST_CLOCK_TIME_IS_VALID (parse->last_stop_end) || + parse->last_stop_end < last_stop_end) + parse->last_stop_end = last_stop_end; + + if (parse->segment.duration == -1 || + parse->segment.duration < lace_time) { + gst_segment_set_duration (&parse->segment, GST_FORMAT_TIME, + last_stop_end); + gst_element_post_message (GST_ELEMENT_CAST (parse), + gst_message_new_duration (GST_OBJECT_CAST (parse), + GST_FORMAT_TIME, GST_CLOCK_TIME_NONE)); + } + } + + stream->pos = lace_time; + + gst_matroska_parse_sync_streams (parse); + + if (stream->set_discont) { + GST_DEBUG_OBJECT (parse, "marking DISCONT"); + GST_BUFFER_FLAG_SET (sub, GST_BUFFER_FLAG_DISCONT); + stream->set_discont = FALSE; + } + + /* reverse playback book-keeping */ + if (!GST_CLOCK_TIME_IS_VALID (stream->from_time)) + stream->from_time = lace_time; + if (stream->from_offset == -1) + stream->from_offset = offset; + + GST_DEBUG_OBJECT (parse, + "Pushing lace %d, data of size %d for stream %d, time=%" + GST_TIME_FORMAT " and duration=%" GST_TIME_FORMAT, n, + GST_BUFFER_SIZE (sub), stream_num, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (sub)), + GST_TIME_ARGS (GST_BUFFER_DURATION (sub))); + + if (parse->element_index) { + if (stream->index_writer_id == -1) + gst_index_get_writer_id (parse->element_index, + GST_OBJECT (stream->pad), &stream->index_writer_id); + + GST_LOG_OBJECT (parse, "adding association %" GST_TIME_FORMAT "-> %" + G_GUINT64_FORMAT " for writer id %d", + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (sub)), cluster_offset, + stream->index_writer_id); + gst_index_add_association (parse->element_index, + stream->index_writer_id, GST_BUFFER_FLAG_IS_SET (sub, + GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : GST_ASSOCIATION_FLAG_KEY_UNIT, + GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (sub), GST_FORMAT_BYTES, + cluster_offset, NULL); + } + + gst_buffer_set_caps (sub, GST_PAD_CAPS (parse->srcpad)); + + /* Postprocess the buffers depending on the codec used */ + if (stream->postprocess_frame) { + GST_LOG_OBJECT (parse, "running post process"); + ret = stream->postprocess_frame (GST_ELEMENT (parse), stream, &sub); + } + + ret = gst_pad_push (stream->pad, sub); + if (parse->segment.rate < 0) { + if (lace_time > parse->segment.stop && ret == GST_FLOW_UNEXPECTED) { + /* In reverse playback we can get a GST_FLOW_UNEXPECTED when + * we are at the end of the segment, so we just need to jump + * back to the previous section. */ + GST_DEBUG_OBJECT (parse, "downstream has reached end of segment"); + ret = GST_FLOW_OK; + } + } + /* combine flows */ + ret = gst_matroska_parse_combine_flows (parse, stream, ret); +#endif + + next_lace: + size -= lace_size[n]; + if (lace_time != GST_CLOCK_TIME_NONE && duration) + lace_time += duration / laces; + else + lace_time = GST_CLOCK_TIME_NONE; + } + } + +done: + if (buf) + gst_buffer_unref (buf); + g_free (lace_size); + + return ret; + + /* EXITS */ +invalid_lacing: + { + GST_ELEMENT_WARNING (parse, STREAM, DEMUX, (NULL), ("Invalid lacing size")); + /* non-fatal, try next block(group) */ + ret = GST_FLOW_OK; + goto done; + } +data_error: + { + GST_ELEMENT_WARNING (parse, STREAM, DEMUX, (NULL), ("Data error")); + /* non-fatal, try next block(group) */ + ret = GST_FLOW_OK; + goto done; + } +} + +/* return FALSE if block(group) should be skipped (due to a seek) */ +static inline gboolean +gst_matroska_parse_seek_block (GstMatroskaParse * parse) +{ + if (G_UNLIKELY (parse->seek_block)) { + if (!(--parse->seek_block)) { + return TRUE; + } else { + GST_LOG_OBJECT (parse, "should skip block due to seek"); + return FALSE; + } + } else { + return TRUE; + } +} + +static GstFlowReturn +gst_matroska_parse_parse_contents_seekentry (GstMatroskaParse * parse, + GstEbmlRead * ebml) +{ + GstFlowReturn ret; + guint64 seek_pos = (guint64) - 1; + guint32 seek_id = 0; + guint32 id; + + DEBUG_ELEMENT_START (parse, ebml, "Seek"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "Seek", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + case GST_MATROSKA_ID_SEEKID: + { + guint64 t; + + if ((ret = gst_ebml_read_uint (ebml, &id, &t)) != GST_FLOW_OK) + break; + + GST_DEBUG_OBJECT (parse, "SeekID: %" G_GUINT64_FORMAT, t); + seek_id = t; + break; + } + + case GST_MATROSKA_ID_SEEKPOSITION: + { + guint64 t; + + if ((ret = gst_ebml_read_uint (ebml, &id, &t)) != GST_FLOW_OK) + break; + + if (t > G_MAXINT64) { + GST_WARNING_OBJECT (parse, + "Too large SeekPosition %" G_GUINT64_FORMAT, t); + break; + } + + GST_DEBUG_OBJECT (parse, "SeekPosition: %" G_GUINT64_FORMAT, t); + seek_pos = t; + break; + } + + default: + ret = gst_matroska_parse_parse_skip (parse, ebml, "SeekHead", id); + break; + } + } + + if (ret != GST_FLOW_OK && ret != GST_FLOW_UNEXPECTED) + return ret; + + if (!seek_id || seek_pos == (guint64) - 1) { + GST_WARNING_OBJECT (parse, "Incomplete seekhead entry (0x%x/%" + G_GUINT64_FORMAT ")", seek_id, seek_pos); + return GST_FLOW_OK; + } + + switch (seek_id) { + case GST_MATROSKA_ID_SEEKHEAD: + { + } + case GST_MATROSKA_ID_CUES: + case GST_MATROSKA_ID_TAGS: + case GST_MATROSKA_ID_TRACKS: + case GST_MATROSKA_ID_SEGMENTINFO: + case GST_MATROSKA_ID_ATTACHMENTS: + case GST_MATROSKA_ID_CHAPTERS: + { + guint64 before_pos, length; + + /* remember */ + length = gst_matroska_parse_get_length (parse); + before_pos = parse->offset; + + if (length == (guint64) - 1) { + GST_DEBUG_OBJECT (parse, "no upstream length, skipping SeakHead entry"); + break; + } + + /* check for validity */ + if (seek_pos + parse->ebml_segment_start + 12 >= length) { + GST_WARNING_OBJECT (parse, + "SeekHead reference lies outside file!" " (%" + G_GUINT64_FORMAT "+%" G_GUINT64_FORMAT "+12 >= %" + G_GUINT64_FORMAT ")", seek_pos, parse->ebml_segment_start, length); + break; + } + + /* only pick up index location when streaming */ + if (seek_id == GST_MATROSKA_ID_CUES) { + parse->index_offset = seek_pos + parse->ebml_segment_start; + GST_DEBUG_OBJECT (parse, "Cues located at offset %" G_GUINT64_FORMAT, + parse->index_offset); + } + break; + } + + default: + GST_DEBUG_OBJECT (parse, "Ignoring Seek entry for ID=0x%x", seek_id); + break; + } + DEBUG_ELEMENT_STOP (parse, ebml, "Seek", ret); + + return ret; +} + +static GstFlowReturn +gst_matroska_parse_parse_contents (GstMatroskaParse * parse, GstEbmlRead * ebml) +{ + GstFlowReturn ret = GST_FLOW_OK; + guint32 id; + + DEBUG_ELEMENT_START (parse, ebml, "SeekHead"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (parse, ebml, "SeekHead", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + case GST_MATROSKA_ID_SEEKENTRY: + { + ret = gst_matroska_parse_parse_contents_seekentry (parse, ebml); + /* Ignore EOS and errors here */ + if (ret != GST_FLOW_OK) { + GST_DEBUG_OBJECT (parse, "Ignoring %s", gst_flow_get_name (ret)); + ret = GST_FLOW_OK; + } + break; + } + + default: + ret = gst_matroska_parse_parse_skip (parse, ebml, "SeekHead", id); + break; + } + } + + DEBUG_ELEMENT_STOP (parse, ebml, "SeekHead", ret); + + return ret; +} + +#define GST_FLOW_OVERFLOW GST_FLOW_CUSTOM_ERROR + +#define MAX_BLOCK_SIZE (15 * 1024 * 1024) + +static inline GstFlowReturn +gst_matroska_parse_check_read_size (GstMatroskaParse * parse, guint64 bytes) +{ + if (G_UNLIKELY (bytes > MAX_BLOCK_SIZE)) { + /* only a few blocks are expected/allowed to be large, + * and will be recursed into, whereas others will be read and must fit */ + /* fatal in streaming case, as we can't step over easily */ + GST_ELEMENT_ERROR (parse, STREAM, DEMUX, (NULL), + ("reading large block of size %" G_GUINT64_FORMAT " not supported; " + "file might be corrupt.", bytes)); + return GST_FLOW_ERROR; + } else { + return GST_FLOW_OK; + } +} + +/* returns TRUE if we truely are in error state, and should give up */ +static inline gboolean +gst_matroska_parse_check_parse_error (GstMatroskaParse * parse) +{ + gint64 pos; + + /* sigh, one last attempt above and beyond call of duty ...; + * search for cluster mark following current pos */ + pos = parse->offset; + GST_WARNING_OBJECT (parse, "parse error, looking for next cluster"); + if (gst_matroska_parse_search_cluster (parse, &pos) != GST_FLOW_OK) { + /* did not work, give up */ + return TRUE; + } else { + GST_DEBUG_OBJECT (parse, "... found at %" G_GUINT64_FORMAT, pos); + /* try that position */ + parse->offset = pos; + return FALSE; + } +} + +/* initializes @ebml with @bytes from input stream at current offset. + * Returns UNEXPECTED if insufficient available, + * ERROR if too much was attempted to read. */ +static inline GstFlowReturn +gst_matroska_parse_take (GstMatroskaParse * parse, guint64 bytes, + GstEbmlRead * ebml) +{ + GstBuffer *buffer = NULL; + GstFlowReturn ret = GST_FLOW_OK; + + GST_LOG_OBJECT (parse, "taking %" G_GUINT64_FORMAT " bytes for parsing", + bytes); + ret = gst_matroska_parse_check_read_size (parse, bytes); + if (G_UNLIKELY (ret != GST_FLOW_OK)) { + /* otherwise fatal */ + ret = GST_FLOW_ERROR; + goto exit; + } + if (gst_adapter_available (parse->adapter) >= bytes) + buffer = gst_adapter_take_buffer (parse->adapter, bytes); + else + ret = GST_FLOW_UNEXPECTED; + if (G_LIKELY (buffer)) { + gst_ebml_read_init (ebml, GST_ELEMENT_CAST (parse), buffer, parse->offset); + parse->offset += bytes; + } +exit: + return ret; +} + +static void +gst_matroska_parse_check_seekability (GstMatroskaParse * parse) +{ + GstQuery *query; + gboolean seekable = FALSE; + gint64 start = -1, stop = -1; + + query = gst_query_new_seeking (GST_FORMAT_BYTES); + if (!gst_pad_peer_query (parse->sinkpad, query)) { + GST_DEBUG_OBJECT (parse, "seeking query failed"); + goto done; + } + + gst_query_parse_seeking (query, NULL, &seekable, &start, &stop); + + /* try harder to query upstream size if we didn't get it the first time */ + if (seekable && stop == -1) { + GstFormat fmt = GST_FORMAT_BYTES; + + GST_DEBUG_OBJECT (parse, "doing duration query to fix up unset stop"); + gst_pad_query_peer_duration (parse->sinkpad, &fmt, &stop); + } + + /* if upstream doesn't know the size, it's likely that it's not seekable in + * practice even if it technically may be seekable */ + if (seekable && (start != 0 || stop <= start)) { + GST_DEBUG_OBJECT (parse, "seekable but unknown start/stop -> disable"); + seekable = FALSE; + } + +done: + GST_INFO_OBJECT (parse, "seekable: %d (%" G_GUINT64_FORMAT " - %" + G_GUINT64_FORMAT ")", seekable, start, stop); + parse->seekable = seekable; + + gst_query_unref (query); +} + +#if 0 +static GstFlowReturn +gst_matroska_parse_find_tracks (GstMatroskaParse * parse) +{ + guint32 id; + guint64 before_pos; + guint64 length; + guint needed; + GstFlowReturn ret = GST_FLOW_OK; + + GST_WARNING_OBJECT (parse, + "Found Cluster element before Tracks, searching Tracks"); + + /* remember */ + before_pos = parse->offset; + + /* Search Tracks element */ + while (TRUE) { + ret = gst_matroska_parse_peek_id_length_pull (parse, &id, &length, &needed); + if (ret != GST_FLOW_OK) + break; + + if (id != GST_MATROSKA_ID_TRACKS) { + /* we may be skipping large cluster here, so forego size check etc */ + /* ... but we can't skip undefined size; force error */ + if (length == G_MAXUINT64) { + ret = gst_matroska_parse_check_read_size (parse, length); + break; + } else { + parse->offset += needed; + parse->offset += length; + } + continue; + } + + /* will lead to track parsing ... */ + ret = gst_matroska_parse_parse_id (parse, id, length, needed); + break; + } + + /* seek back */ + parse->offset = before_pos; + + return ret; +} +#endif + +#define GST_READ_CHECK(stmt) \ +G_STMT_START { \ + if (G_UNLIKELY ((ret = (stmt)) != GST_FLOW_OK)) { \ + if (ret == GST_FLOW_OVERFLOW) { \ + ret = GST_FLOW_OK; \ + } \ + goto read_error; \ + } \ +} G_STMT_END + +static void +gst_matroska_parse_accumulate_streamheader (GstMatroskaParse * parse, + GstBuffer * buffer) +{ + if (parse->streamheader) { + GstBuffer *buf; + + buf = gst_buffer_span (parse->streamheader, 0, buffer, + GST_BUFFER_SIZE (parse->streamheader) + GST_BUFFER_SIZE (buffer)); + gst_buffer_unref (parse->streamheader); + parse->streamheader = buf; + } else { + parse->streamheader = gst_buffer_ref (buffer); + } + + GST_DEBUG ("%d", GST_BUFFER_SIZE (parse->streamheader)); +} + +static GstFlowReturn +gst_matroska_parse_output (GstMatroskaParse * parse, GstBuffer * buffer, + gboolean keyframe) +{ + GstFlowReturn ret = GST_FLOW_OK; + + if (!parse->pushed_headers) { + GstCaps *caps; + GstStructure *s; + GValue streamheader = { 0 }; + GValue bufval = { 0 }; + GstBuffer *buf; + + caps = gst_caps_new_simple ("video/x-matroska", NULL); + s = gst_caps_get_structure (caps, 0); + g_value_init (&streamheader, GST_TYPE_ARRAY); + g_value_init (&bufval, GST_TYPE_BUFFER); + GST_BUFFER_FLAG_SET (parse->streamheader, GST_BUFFER_FLAG_IN_CAPS); + gst_value_set_buffer (&bufval, parse->streamheader); + gst_value_array_append_value (&streamheader, &bufval); + g_value_unset (&bufval); + gst_structure_set_value (s, "streamheader", &streamheader); + g_value_unset (&streamheader); + //gst_caps_replace (parse->caps, caps); + gst_pad_set_caps (parse->srcpad, caps); + + buf = gst_buffer_make_metadata_writable (parse->streamheader); + gst_buffer_set_caps (buf, caps); + gst_caps_unref (caps); + + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); + + ret = gst_pad_push (parse->srcpad, buf); + + parse->pushed_headers = TRUE; + } + + if (!keyframe) { + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); + } else { + GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); + } + if (GST_BUFFER_TIMESTAMP (buffer) != GST_CLOCK_TIME_NONE) { + parse->last_timestamp = GST_BUFFER_TIMESTAMP (buffer); + } else { + GST_BUFFER_TIMESTAMP (buffer) = parse->last_timestamp; + } + gst_buffer_set_caps (buffer, GST_PAD_CAPS (parse->srcpad)); + ret = gst_pad_push (parse->srcpad, gst_buffer_ref (buffer)); + + return ret; +} + +static GstFlowReturn +gst_matroska_parse_parse_id (GstMatroskaParse * parse, guint32 id, + guint64 length, guint needed) +{ + GstEbmlRead ebml = { 0, }; + GstFlowReturn ret = GST_FLOW_OK; + guint64 read; + //GstBuffer *buffer; + + GST_DEBUG_OBJECT (parse, "Parsing Element id 0x%x, " + "size %" G_GUINT64_FORMAT ", prefix %d", id, length, needed); + +#if 0 + if (gst_adapter_available (parse->adapter) >= length + needed) { + buffer = gst_adapter_take_buffer (parse->adapter, length + needed); + gst_pad_push (parse->srcpad, buffer); + } else { + ret = GST_FLOW_UNEXPECTED; + } + //GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); + + return ret; +#endif + + + + /* if we plan to read and parse this element, we need prefix (id + length) + * and the contents */ + /* mind about overflow wrap-around when dealing with undefined size */ + read = length; + if (G_LIKELY (length != G_MAXUINT64)) + read += needed; + + switch (parse->state) { + case GST_MATROSKA_PARSE_STATE_START: + switch (id) { + case GST_EBML_ID_HEADER: + GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); + ret = gst_matroska_parse_parse_header (parse, &ebml); + if (ret != GST_FLOW_OK) + goto parse_failed; + parse->state = GST_MATROSKA_PARSE_STATE_SEGMENT; + gst_matroska_parse_check_seekability (parse); + gst_matroska_parse_accumulate_streamheader (parse, ebml.buf); + break; + default: + goto invalid_header; + break; + } + break; + case GST_MATROSKA_PARSE_STATE_SEGMENT: + switch (id) { + case GST_MATROSKA_ID_SEGMENT: + /* eat segment prefix */ + GST_READ_CHECK (gst_matroska_parse_take (parse, needed, &ebml)); + GST_DEBUG_OBJECT (parse, + "Found Segment start at offset %" G_GUINT64_FORMAT, + parse->offset); + /* seeks are from the beginning of the segment, + * after the segment ID/length */ + parse->ebml_segment_start = parse->offset; + parse->state = GST_MATROSKA_PARSE_STATE_HEADER; + gst_matroska_parse_accumulate_streamheader (parse, ebml.buf); + break; + default: + GST_WARNING_OBJECT (parse, + "Expected a Segment ID (0x%x), but received 0x%x!", + GST_MATROSKA_ID_SEGMENT, id); + GST_READ_CHECK (gst_matroska_parse_take (parse, needed, &ebml)); + gst_matroska_parse_accumulate_streamheader (parse, ebml.buf); + break; + } + break; + case GST_MATROSKA_PARSE_STATE_SCANNING: + if (id != GST_MATROSKA_ID_CLUSTER && + id != GST_MATROSKA_ID_CLUSTERTIMECODE) + goto skip; + /* fall-through */ + case GST_MATROSKA_PARSE_STATE_HEADER: + case GST_MATROSKA_PARSE_STATE_DATA: + case GST_MATROSKA_PARSE_STATE_SEEK: + switch (id) { + case GST_MATROSKA_ID_SEGMENTINFO: + GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); + if (!parse->segmentinfo_parsed) { + ret = gst_matroska_parse_parse_info (parse, &ebml); + } + gst_matroska_parse_accumulate_streamheader (parse, ebml.buf); + break; + case GST_MATROSKA_ID_TRACKS: + GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); + if (!parse->tracks_parsed) { + ret = gst_matroska_parse_parse_tracks (parse, &ebml); + } + gst_matroska_parse_accumulate_streamheader (parse, ebml.buf); + break; + case GST_MATROSKA_ID_CLUSTER: + if (G_UNLIKELY (!parse->tracks_parsed)) { + GST_DEBUG_OBJECT (parse, "Cluster before Track"); + goto not_streamable; + } + if (G_UNLIKELY (parse->state == GST_MATROSKA_PARSE_STATE_HEADER)) { + parse->state = GST_MATROSKA_PARSE_STATE_DATA; + parse->first_cluster_offset = parse->offset; + GST_DEBUG_OBJECT (parse, "signaling no more pads"); + } + parse->cluster_time = GST_CLOCK_TIME_NONE; + parse->cluster_offset = parse->offset; + if (G_UNLIKELY (!parse->seek_first && parse->seek_block)) { + GST_DEBUG_OBJECT (parse, "seek target block %" G_GUINT64_FORMAT + " not found in Cluster, trying next Cluster's first block instead", + parse->seek_block); + parse->seek_block = 0; + } + parse->seek_first = FALSE; + /* record next cluster for recovery */ + if (read != G_MAXUINT64) + parse->next_cluster_offset = parse->cluster_offset + read; + /* eat cluster prefix */ + GST_READ_CHECK (gst_matroska_parse_take (parse, needed, &ebml)); + ret = gst_matroska_parse_output (parse, ebml.buf, TRUE); + //gst_matroska_parse_accumulate_streamheader (parse, ebml.buf); + break; + case GST_MATROSKA_ID_CLUSTERTIMECODE: + { + guint64 num; + + GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); + if ((ret = gst_ebml_read_uint (&ebml, &id, &num)) != GST_FLOW_OK) + goto parse_failed; + GST_DEBUG_OBJECT (parse, "ClusterTimeCode: %" G_GUINT64_FORMAT, num); + parse->cluster_time = num; + if (parse->element_index) { + if (parse->element_index_writer_id == -1) + gst_index_get_writer_id (parse->element_index, + GST_OBJECT (parse), &parse->element_index_writer_id); + GST_LOG_OBJECT (parse, "adding association %" GST_TIME_FORMAT "-> %" + G_GUINT64_FORMAT " for writer id %d", + GST_TIME_ARGS (parse->cluster_time), parse->cluster_offset, + parse->element_index_writer_id); + gst_index_add_association (parse->element_index, + parse->element_index_writer_id, GST_ASSOCIATION_FLAG_KEY_UNIT, + GST_FORMAT_TIME, parse->cluster_time, + GST_FORMAT_BYTES, parse->cluster_offset, NULL); + } + gst_matroska_parse_output (parse, ebml.buf, FALSE); + break; + } + case GST_MATROSKA_ID_BLOCKGROUP: + if (!gst_matroska_parse_seek_block (parse)) + goto skip; + GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); + DEBUG_ELEMENT_START (parse, &ebml, "BlockGroup"); + if ((ret = gst_ebml_read_master (&ebml, &id)) == GST_FLOW_OK) { + ret = gst_matroska_parse_parse_blockgroup_or_simpleblock (parse, + &ebml, parse->cluster_time, parse->cluster_offset, FALSE); + } + DEBUG_ELEMENT_STOP (parse, &ebml, "BlockGroup", ret); + gst_matroska_parse_output (parse, ebml.buf, FALSE); + break; + case GST_MATROSKA_ID_SIMPLEBLOCK: + if (!gst_matroska_parse_seek_block (parse)) + goto skip; + GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); + DEBUG_ELEMENT_START (parse, &ebml, "SimpleBlock"); + ret = gst_matroska_parse_parse_blockgroup_or_simpleblock (parse, + &ebml, parse->cluster_time, parse->cluster_offset, TRUE); + DEBUG_ELEMENT_STOP (parse, &ebml, "SimpleBlock", ret); + gst_matroska_parse_output (parse, ebml.buf, FALSE); + break; + case GST_MATROSKA_ID_ATTACHMENTS: + GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); + if (!parse->attachments_parsed) { + ret = gst_matroska_parse_parse_attachments (parse, &ebml); + } + gst_matroska_parse_output (parse, ebml.buf, FALSE); + break; + case GST_MATROSKA_ID_TAGS: + GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); + ret = gst_matroska_parse_parse_metadata (parse, &ebml); + gst_matroska_parse_output (parse, ebml.buf, FALSE); + break; + case GST_MATROSKA_ID_CHAPTERS: + GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); + ret = gst_matroska_parse_parse_chapters (parse, &ebml); + gst_matroska_parse_output (parse, ebml.buf, FALSE); + break; + case GST_MATROSKA_ID_SEEKHEAD: + GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); + ret = gst_matroska_parse_parse_contents (parse, &ebml); + gst_matroska_parse_output (parse, ebml.buf, FALSE); + break; + case GST_MATROSKA_ID_CUES: + GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); + if (!parse->index_parsed) { + ret = gst_matroska_parse_parse_index (parse, &ebml); + /* only push based; delayed index building */ + if (ret == GST_FLOW_OK + && parse->state == GST_MATROSKA_PARSE_STATE_SEEK) { + GstEvent *event; + + GST_OBJECT_LOCK (parse); + event = parse->seek_event; + parse->seek_event = NULL; + GST_OBJECT_UNLOCK (parse); + + g_assert (event); + /* unlikely to fail, since we managed to seek to this point */ + if (!gst_matroska_parse_handle_seek_event (parse, NULL, event)) + goto seek_failed; + /* resume data handling, main thread clear to seek again */ + GST_OBJECT_LOCK (parse); + parse->state = GST_MATROSKA_PARSE_STATE_DATA; + GST_OBJECT_UNLOCK (parse); + } + } + gst_matroska_parse_output (parse, ebml.buf, FALSE); + break; + case GST_MATROSKA_ID_POSITION: + case GST_MATROSKA_ID_PREVSIZE: + case GST_MATROSKA_ID_ENCRYPTEDBLOCK: + case GST_MATROSKA_ID_SILENTTRACKS: + GST_DEBUG_OBJECT (parse, + "Skipping Cluster subelement 0x%x - ignoring", id); + /* fall-through */ + default: + skip: + GST_DEBUG_OBJECT (parse, "skipping Element 0x%x", id); + GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); + gst_matroska_parse_output (parse, ebml.buf, FALSE); + break; + } + break; + } + + if (ret == GST_FLOW_PARSE) + goto parse_failed; + +exit: + gst_ebml_read_clear (&ebml); + return ret; + + /* ERRORS */ +read_error: + { + /* simply exit, maybe not enough data yet */ + /* no ebml to clear if read error */ + return ret; + } +parse_failed: + { + GST_ELEMENT_ERROR (parse, STREAM, DEMUX, (NULL), + ("Failed to parse Element 0x%x", id)); + ret = GST_FLOW_ERROR; + goto exit; + } +not_streamable: + { + GST_ELEMENT_ERROR (parse, STREAM, DEMUX, (NULL), + ("File layout does not permit streaming")); + ret = GST_FLOW_ERROR; + goto exit; + } +#if 0 +no_tracks: + { + GST_ELEMENT_ERROR (parse, STREAM, DEMUX, (NULL), + ("No Tracks element found")); + ret = GST_FLOW_ERROR; + goto exit; + } +#endif +invalid_header: + { + GST_ELEMENT_ERROR (parse, STREAM, DEMUX, (NULL), ("Invalid header")); + ret = GST_FLOW_ERROR; + goto exit; + } +seek_failed: + { + GST_ELEMENT_ERROR (parse, STREAM, DEMUX, (NULL), ("Failed to seek")); + ret = GST_FLOW_ERROR; + goto exit; + } +} + +#if 0 +static void +gst_matroska_parse_loop (GstPad * pad) +{ + GstMatroskaParse *parse = GST_MATROSKA_PARSE (GST_PAD_PARENT (pad)); + GstFlowReturn ret; + guint32 id; + guint64 length; + guint needed; + + /* If we have to close a segment, send a new segment to do this now */ + if (G_LIKELY (parse->state == GST_MATROSKA_PARSE_STATE_DATA)) { + if (G_UNLIKELY (parse->close_segment)) { + gst_matroska_parse_send_event (parse, parse->close_segment); + parse->close_segment = NULL; + } + if (G_UNLIKELY (parse->new_segment)) { + gst_matroska_parse_send_event (parse, parse->new_segment); + parse->new_segment = NULL; + } + } + + ret = gst_matroska_parse_peek_id_length_pull (parse, &id, &length, &needed); + if (ret == GST_FLOW_UNEXPECTED) + goto eos; + if (ret != GST_FLOW_OK) { + if (gst_matroska_parse_check_parse_error (parse)) + goto pause; + else + return; + } + + GST_LOG_OBJECT (parse, "Offset %" G_GUINT64_FORMAT ", Element id 0x%x, " + "size %" G_GUINT64_FORMAT ", needed %d", parse->offset, id, + length, needed); + + ret = gst_matroska_parse_parse_id (parse, id, length, needed); + if (ret == GST_FLOW_UNEXPECTED) + goto eos; + if (ret != GST_FLOW_OK) + goto pause; + + /* check if we're at the end of a configured segment */ + if (G_LIKELY (parse->src->len)) { + guint i; + + g_assert (parse->num_streams == parse->src->len); + for (i = 0; i < parse->src->len; i++) { + GstMatroskaTrackContext *context = g_ptr_array_index (parse->src, i); + GST_DEBUG_OBJECT (context->pad, "pos %" GST_TIME_FORMAT, + GST_TIME_ARGS (context->pos)); + if (context->eos == FALSE) + goto next; + } + + GST_INFO_OBJECT (parse, "All streams are EOS"); + ret = GST_FLOW_UNEXPECTED; + goto eos; + } + +next: + if (G_UNLIKELY (parse->offset == gst_matroska_parse_get_length (parse))) { + GST_LOG_OBJECT (parse, "Reached end of stream"); + ret = GST_FLOW_UNEXPECTED; + goto eos; + } + + return; + + /* ERRORS */ +eos: + { + if (parse->segment.rate < 0.0) { + ret = gst_matroska_parse_seek_to_previous_keyframe (parse); + if (ret == GST_FLOW_OK) + return; + } + /* fall-through */ + } +pause: + { + const gchar *reason = gst_flow_get_name (ret); + gboolean push_eos = FALSE; + + GST_LOG_OBJECT (parse, "pausing task, reason %s", reason); + parse->segment_running = FALSE; + gst_pad_pause_task (parse->sinkpad); + + if (ret == GST_FLOW_UNEXPECTED) { + /* perform EOS logic */ + + /* Close the segment, i.e. update segment stop with the duration + * if no stop was set */ + if (GST_CLOCK_TIME_IS_VALID (parse->last_stop_end) && + !GST_CLOCK_TIME_IS_VALID (parse->segment.stop)) { + GstEvent *event = + gst_event_new_new_segment_full (TRUE, parse->segment.rate, + parse->segment.applied_rate, parse->segment.format, + parse->segment.start, + MAX (parse->last_stop_end, parse->segment.start), + parse->segment.time); + gst_matroska_parse_send_event (parse, event); + } + + if (parse->segment.flags & GST_SEEK_FLAG_SEGMENT) { + gint64 stop; + + /* for segment playback we need to post when (in stream time) + * we stopped, this is either stop (when set) or the duration. */ + if ((stop = parse->segment.stop) == -1) + stop = parse->last_stop_end; + + GST_LOG_OBJECT (parse, "Sending segment done, at end of segment"); + gst_element_post_message (GST_ELEMENT (parse), + gst_message_new_segment_done (GST_OBJECT (parse), GST_FORMAT_TIME, + stop)); + } else { + push_eos = TRUE; + } + } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_UNEXPECTED) { + /* for fatal errors we post an error message */ + GST_ELEMENT_ERROR (parse, STREAM, FAILED, (NULL), + ("stream stopped, reason %s", reason)); + push_eos = TRUE; + } + if (push_eos) { + /* send EOS, and prevent hanging if no streams yet */ + GST_LOG_OBJECT (parse, "Sending EOS, at end of stream"); + if (!gst_matroska_parse_send_event (parse, gst_event_new_eos ()) && + (ret == GST_FLOW_UNEXPECTED)) { + GST_ELEMENT_ERROR (parse, STREAM, DEMUX, + (NULL), ("got eos but no streams (yet)")); + } + } + return; + } +} +#endif + +/* + * Create and push a flushing seek event upstream + */ +static gboolean +perform_seek_to_offset (GstMatroskaParse * parse, guint64 offset) +{ + GstEvent *event; + gboolean res = 0; + + GST_DEBUG_OBJECT (parse, "Seeking to %" G_GUINT64_FORMAT, offset); + + event = + gst_event_new_seek (1.0, GST_FORMAT_BYTES, + GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset, + GST_SEEK_TYPE_NONE, -1); + + res = gst_pad_push_event (parse->sinkpad, event); + + /* newsegment event will update offset */ + return res; +} + +static const guint8 * +gst_matroska_parse_peek_adapter (GstMatroskaParse * parse, guint peek) +{ + return gst_adapter_peek (parse->adapter, peek); +} + +static GstFlowReturn +gst_matroska_parse_peek_id_length_push (GstMatroskaParse * parse, guint32 * _id, + guint64 * _length, guint * _needed) +{ + return gst_ebml_peek_id_length (_id, _length, _needed, + (GstPeekData) gst_matroska_parse_peek_adapter, (gpointer) parse, + GST_ELEMENT_CAST (parse), parse->offset); +} + +static GstFlowReturn +gst_matroska_parse_chain (GstPad * pad, GstBuffer * buffer) +{ + GstMatroskaParse *parse = GST_MATROSKA_PARSE (GST_PAD_PARENT (pad)); + guint available; + GstFlowReturn ret = GST_FLOW_OK; + guint needed = 0; + guint32 id; + guint64 length; + + if (G_UNLIKELY (GST_BUFFER_IS_DISCONT (buffer))) { + GST_DEBUG_OBJECT (parse, "got DISCONT"); + gst_adapter_clear (parse->adapter); + GST_OBJECT_LOCK (parse); + gst_matroska_parse_reset_streams (parse, GST_CLOCK_TIME_NONE, FALSE); + GST_OBJECT_UNLOCK (parse); + } + + gst_adapter_push (parse->adapter, buffer); + buffer = NULL; + +next: + available = gst_adapter_available (parse->adapter); + + ret = gst_matroska_parse_peek_id_length_push (parse, &id, &length, &needed); + if (G_UNLIKELY (ret != GST_FLOW_OK && ret != GST_FLOW_UNEXPECTED)) + return ret; + + GST_LOG_OBJECT (parse, "Offset %" G_GUINT64_FORMAT ", Element id 0x%x, " + "size %" G_GUINT64_FORMAT ", needed %d, available %d", parse->offset, id, + length, needed, available); + + if (needed > available) + return GST_FLOW_OK; + + ret = gst_matroska_parse_parse_id (parse, id, length, needed); + if (ret == GST_FLOW_UNEXPECTED) { + /* need more data */ + return GST_FLOW_OK; + } else if (ret != GST_FLOW_OK) { + return ret; + } else + goto next; +} + +static gboolean +gst_matroska_parse_handle_sink_event (GstPad * pad, GstEvent * event) +{ + gboolean res = TRUE; + GstMatroskaParse *parse = GST_MATROSKA_PARSE (GST_PAD_PARENT (pad)); + + GST_DEBUG_OBJECT (parse, + "have event type %s: %p on sink pad", GST_EVENT_TYPE_NAME (event), event); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT: + { + GstFormat format; + gdouble rate, arate; + gint64 start, stop, time = 0; + 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 (parse, + "received format %d newsegment %" GST_SEGMENT_FORMAT, format, + &segment); + + if (parse->state < GST_MATROSKA_PARSE_STATE_DATA) { + GST_DEBUG_OBJECT (parse, "still starting"); + goto exit; + } + + /* we only expect a BYTE segment, e.g. following a seek */ + if (format != GST_FORMAT_BYTES) { + GST_DEBUG_OBJECT (parse, "unsupported segment format, ignoring"); + goto exit; + } + + GST_DEBUG_OBJECT (parse, "clearing segment state"); + /* clear current segment leftover */ + gst_adapter_clear (parse->adapter); + /* and some streaming setup */ + parse->offset = start; + /* do not know where we are; + * need to come across a cluster and generate newsegment */ + parse->segment.last_stop = GST_CLOCK_TIME_NONE; + parse->cluster_time = GST_CLOCK_TIME_NONE; + parse->cluster_offset = 0; + parse->need_newsegment = TRUE; + /* but keep some of the upstream segment */ + parse->segment.rate = rate; + exit: + /* chain will send initial newsegment after pads have been added, + * or otherwise come up with one */ + GST_DEBUG_OBJECT (parse, "eating event"); + gst_event_unref (event); + res = TRUE; + break; + } + case GST_EVENT_EOS: + { + if (parse->state != GST_MATROSKA_PARSE_STATE_DATA) { + gst_event_unref (event); + GST_ELEMENT_ERROR (parse, STREAM, DEMUX, + (NULL), ("got eos and didn't receive a complete header object")); + } else if (parse->num_streams == 0) { + GST_ELEMENT_ERROR (parse, STREAM, DEMUX, + (NULL), ("got eos but no streams (yet)")); + } else { + gst_matroska_parse_send_event (parse, event); + } + break; + } + case GST_EVENT_FLUSH_STOP: + { + gst_adapter_clear (parse->adapter); + GST_OBJECT_LOCK (parse); + gst_matroska_parse_reset_streams (parse, GST_CLOCK_TIME_NONE, TRUE); + GST_OBJECT_UNLOCK (parse); + parse->segment.last_stop = GST_CLOCK_TIME_NONE; + parse->cluster_time = GST_CLOCK_TIME_NONE; + parse->cluster_offset = 0; + /* fall-through */ + } + default: + res = gst_pad_event_default (pad, event); + break; + } + + return res; +} + +static void +gst_matroska_parse_set_index (GstElement * element, GstIndex * index) +{ + GstMatroskaParse *parse = GST_MATROSKA_PARSE (element); + + GST_OBJECT_LOCK (parse); + if (parse->element_index) + gst_object_unref (parse->element_index); + parse->element_index = index ? gst_object_ref (index) : NULL; + GST_OBJECT_UNLOCK (parse); + GST_DEBUG_OBJECT (parse, "Set index %" GST_PTR_FORMAT, parse->element_index); +} + +static GstIndex * +gst_matroska_parse_get_index (GstElement * element) +{ + GstIndex *result = NULL; + GstMatroskaParse *parse = GST_MATROSKA_PARSE (element); + + GST_OBJECT_LOCK (parse); + if (parse->element_index) + result = gst_object_ref (parse->element_index); + GST_OBJECT_UNLOCK (parse); + + GST_DEBUG_OBJECT (parse, "Returning index %" GST_PTR_FORMAT, result); + + return result; +} + +static GstStateChangeReturn +gst_matroska_parse_change_state (GstElement * element, + GstStateChange transition) +{ + GstMatroskaParse *parse = GST_MATROSKA_PARSE (element); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + /* handle upwards state changes here */ + switch (transition) { + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + /* handle downwards state changes */ + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_matroska_parse_reset (GST_ELEMENT (parse)); + break; + default: + break; + } + + return ret; +} + +gboolean +gst_matroska_parse_plugin_init (GstPlugin * plugin) +{ + gst_riff_init (); + + /* create an elementfactory for the matroska_parse element */ + if (!gst_element_register (plugin, "matroskaparse", + GST_RANK_NONE, GST_TYPE_MATROSKA_PARSE)) + return FALSE; + + return TRUE; +} diff --git a/gst/matroska/matroska-parse.h b/gst/matroska/matroska-parse.h new file mode 100644 index 0000000000..595ead2e3c --- /dev/null +++ b/gst/matroska/matroska-parse.h @@ -0,0 +1,146 @@ +/* GStreamer Matroska muxer/demuxer + * (c) 2003 Ronald Bultje + * + * matroska-parse.h: matroska file/stream parseer definition + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_MATROSKA_PARSE_H__ +#define __GST_MATROSKA_PARSE_H__ + +#include +#include + +#include "ebml-read.h" +#include "matroska-ids.h" + +G_BEGIN_DECLS + +#define GST_TYPE_MATROSKA_PARSE \ + (gst_matroska_parse_get_type ()) +#define GST_MATROSKA_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MATROSKA_PARSE, GstMatroskaParse)) +#define GST_MATROSKA_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MATROSKA_PARSE, GstMatroskaParseClass)) +#define GST_IS_MATROSKA_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MATROSKA_PARSE)) +#define GST_IS_MATROSKA_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MATROSKA_PARSE)) + +typedef enum { + GST_MATROSKA_PARSE_STATE_START, + GST_MATROSKA_PARSE_STATE_SEGMENT, + GST_MATROSKA_PARSE_STATE_HEADER, + GST_MATROSKA_PARSE_STATE_DATA, + GST_MATROSKA_PARSE_STATE_SEEK, + GST_MATROSKA_PARSE_STATE_SCANNING +} GstMatroskaParseState; + +typedef struct _GstMatroskaParse { + GstElement parent; + + /* < private > */ + + GstIndex *element_index; + gint element_index_writer_id; + + /* pads */ + GstPad *sinkpad; + GstPad *srcpad; + GPtrArray *src; + GstClock *clock; + guint num_streams; + guint num_v_streams; + guint num_a_streams; + guint num_t_streams; + + GstBuffer *streamheader; + gboolean pushed_headers; + GstClockTime last_timestamp; + + /* metadata */ + gchar *muxing_app; + gchar *writing_app; + gint64 created; + + /* state */ + //gboolean streaming; + GstMatroskaParseState state; + guint level_up; + guint64 seek_block; + gboolean seek_first; + + /* did we parse cues/tracks/segmentinfo already? */ + gboolean index_parsed; + gboolean tracks_parsed; + gboolean segmentinfo_parsed; + gboolean attachments_parsed; + GList *tags_parsed; + GList *seek_parsed; + + /* start-of-segment */ + guint64 ebml_segment_start; + + /* a cue (index) table */ + GArray *index; + + /* timescale in the file */ + guint64 time_scale; + + /* keeping track of playback position */ + GstSegment segment; + gboolean segment_running; + GstClockTime last_stop_end; + + GstEvent *close_segment; + GstEvent *new_segment; + GstTagList *global_tags; + + /* pull mode caching */ + GstBuffer *cached_buffer; + + /* push and pull mode */ + guint64 offset; + /* some state saving */ + GstClockTime cluster_time; + guint64 cluster_offset; + guint64 first_cluster_offset; + guint64 next_cluster_offset; + + /* push based mode usual suspects */ + GstAdapter *adapter; + /* index stuff */ + gboolean seekable; + gboolean building_index; + guint64 index_offset; + GstEvent *seek_event; + gboolean need_newsegment; + + /* reverse playback */ + GArray *seek_index; + gint seek_entry; +} GstMatroskaParse; + +typedef struct _GstMatroskaParseClass { + GstElementClass parent; +} GstMatroskaParseClass; + +gboolean gst_matroska_parse_plugin_init (GstPlugin *plugin); + +G_END_DECLS + +#endif /* __GST_MATROSKA_PARSE_H__ */ diff --git a/gst/matroska/matroska.c b/gst/matroska/matroska.c index fd6c873b8b..57db7a31e0 100644 --- a/gst/matroska/matroska.c +++ b/gst/matroska/matroska.c @@ -24,6 +24,7 @@ #endif #include "matroska-demux.h" +#include "matroska-parse.h" #include "matroska-mux.h" #include "matroska-ids.h" #include "webm-mux.h" @@ -40,6 +41,7 @@ plugin_init (GstPlugin * plugin) gst_matroska_register_tags (); ret = gst_matroska_demux_plugin_init (plugin); + ret &= gst_matroska_parse_plugin_init (plugin); ret &= gst_element_register (plugin, "matroskamux", GST_RANK_PRIMARY, GST_TYPE_MATROSKA_MUX); ret &= gst_element_register (plugin, "webmmux", GST_RANK_PRIMARY, diff --git a/gst/multifile/gstmultifilesink.c b/gst/multifile/gstmultifilesink.c index 2d4ab5b487..b333e809d1 100644 --- a/gst/multifile/gstmultifilesink.c +++ b/gst/multifile/gstmultifilesink.c @@ -146,6 +146,8 @@ static void gst_multi_file_sink_get_property (GObject * object, guint prop_id, static gboolean gst_multi_file_sink_stop (GstBaseSink * sink); static GstFlowReturn gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer); +static gboolean gst_multi_file_sink_set_caps (GstBaseSink * sink, + GstCaps * caps); #define GST_TYPE_MULTI_FILE_SINK_NEXT (gst_multi_file_sink_next_get_type ()) static GType @@ -237,6 +239,8 @@ gst_multi_file_sink_class_init (GstMultiFileSinkClass * klass) gstbasesink_class->get_times = NULL; gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_multi_file_sink_stop); gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_multi_file_sink_render); + gstbasesink_class->set_caps = + GST_DEBUG_FUNCPTR (gst_multi_file_sink_set_caps); } static void @@ -327,6 +331,7 @@ static gboolean gst_multi_file_sink_stop (GstBaseSink * sink) { GstMultiFileSink *multifilesink; + int i; multifilesink = GST_MULTI_FILE_SINK (sink); @@ -335,6 +340,13 @@ gst_multi_file_sink_stop (GstBaseSink * sink) multifilesink->file = NULL; } + if (multifilesink->streamheaders) { + for (i = 0; i < multifilesink->n_streamheaders; i++) { + gst_buffer_unref (multifilesink->streamheaders[i]); + } + g_free (multifilesink->streamheaders); + } + return TRUE; } @@ -462,6 +474,8 @@ gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer) } if (multifilesink->file == NULL) { + int i; + filename = g_strdup_printf (multifilesink->filename, multifilesink->index); multifilesink->file = g_fopen (filename, "wb"); @@ -469,6 +483,16 @@ gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer) if (multifilesink->file == NULL) goto stdio_write_error; + + if (multifilesink->streamheaders) { + for (i = 0; i < multifilesink->n_streamheaders; i++) { + ret = fwrite (GST_BUFFER_DATA (multifilesink->streamheaders[i]), + GST_BUFFER_SIZE (multifilesink->streamheaders[i]), 1, + multifilesink->file); + if (ret != 1) + goto stdio_write_error; + } + } } ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1, @@ -508,3 +532,42 @@ stdio_write_error: ("Error while writing to file."), (NULL)); return GST_FLOW_ERROR; } + +static gboolean +gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps) +{ + GstMultiFileSink *multifilesink; + GstStructure *structure; + + multifilesink = GST_MULTI_FILE_SINK (sink); + + structure = gst_caps_get_structure (caps, 0); + if (structure) { + const GValue *value; + + value = gst_structure_get_value (structure, "streamheader"); + + if (GST_VALUE_HOLDS_ARRAY (value)) { + int i; + + if (multifilesink->streamheaders) { + for (i = 0; i < multifilesink->n_streamheaders; i++) { + gst_buffer_unref (multifilesink->streamheaders[i]); + } + g_free (multifilesink->streamheaders); + } + + multifilesink->n_streamheaders = gst_value_array_get_size (value); + multifilesink->streamheaders = + g_malloc (sizeof (GstBuffer *) * multifilesink->n_streamheaders); + + for (i = 0; i < multifilesink->n_streamheaders; i++) { + multifilesink->streamheaders[i] = + gst_buffer_ref (gst_value_get_buffer (gst_value_array_get_value + (value, i))); + } + } + } + + return TRUE; +} diff --git a/gst/multifile/gstmultifilesink.h b/gst/multifile/gstmultifilesink.h index cc4a4ddf45..c5515706a8 100644 --- a/gst/multifile/gstmultifilesink.h +++ b/gst/multifile/gstmultifilesink.h @@ -69,6 +69,9 @@ struct _GstMultiFileSink FILE *file; gint64 next_segment; + + int n_streamheaders; + GstBuffer **streamheaders; }; struct _GstMultiFileSinkClass diff --git a/gst/qtdemux/qtdemux.c b/gst/qtdemux/qtdemux.c index 3d5498e80b..22803745d5 100644 --- a/gst/qtdemux/qtdemux.c +++ b/gst/qtdemux/qtdemux.c @@ -155,14 +155,14 @@ struct _QtDemuxSample * .-----------------------------------------------------------. * track: | K.....K.........K........K.......K.......K...........K... | * '-----------------------------------------------------------' - * 0 1 2 3 4 + * 0 1 2 3 4 * .------------^ ^ .----------^ ^ * / .-------------' / .------------------' * / / .-----' / * .--------------. .--------------. * | segment 1 | | segment 2 | * '--------------' '--------------' - * + * * The challenge here is to cut out the right pieces of the track for each of * the playback segments. This fortunatly can easily be done with the SEGMENT * events of gstreamer. @@ -174,7 +174,7 @@ struct _QtDemuxSample * * We then proceed to push data from keyframe (a) to frame (b). The decoder * decodes but clips all before media_time 1. - * + * * After finishing a segment, we push out a new SEGMENT event with the clipping * boundaries of the new data. * @@ -387,6 +387,13 @@ GST_BOILERPLATE (GstQTDemux, gst_qtdemux, GstQTDemux, GST_TYPE_ELEMENT); static void gst_qtdemux_dispose (GObject * object); +static guint32 +gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str, + guint64 media_time); +static guint32 +gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux, + QtDemuxStream * str, gint64 media_offset); + static void gst_qtdemux_set_index (GstElement * element, GstIndex * index); static GstIndex *gst_qtdemux_get_index (GstElement * element); static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element, @@ -418,7 +425,7 @@ static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux, gchar ** codec_name); static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n); -static void qtdemux_expose_streams (GstQTDemux * qtdemux); +static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux); static void @@ -459,6 +466,8 @@ gst_qtdemux_class_init (GstQTDemuxClass * klass) gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index); gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index); + + gst_tag_register_musicbrainz_tags (); } static void @@ -561,47 +570,59 @@ gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size, return flow; } -#if 0 +#if 1 static gboolean gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) + GstFormat dest_format, gint64 * dest_value) { gboolean res = TRUE; QtDemuxStream *stream = gst_pad_get_element_private (pad); + GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad)); + gint32 index; - if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e') && - (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) - return FALSE; + if (stream->subtype != FOURCC_vide) { + res = FALSE; + goto done; + } switch (src_format) { case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - *dest_value = src_value * 1; /* FIXME */ - break; - case GST_FORMAT_DEFAULT: - *dest_value = src_value * 1; /* FIXME */ + switch (dest_format) { + case GST_FORMAT_BYTES:{ + index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value); + if (-1 == index) + return FALSE; + + *dest_value = stream->samples[index].offset; + + GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%" + GST_TIME_FORMAT "->%" G_GUINT64_FORMAT, + GST_TIME_ARGS (src_value), *dest_value); break; + } default: res = FALSE; break; } break; case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_TIME: - *dest_value = src_value * 1; /* FIXME */ - break; - default: - res = FALSE; - break; - } - break; - case GST_FORMAT_DEFAULT: - switch (*dest_format) { - case GST_FORMAT_TIME: - *dest_value = src_value * 1; /* FIXME */ + switch (dest_format) { + case GST_FORMAT_TIME:{ + index = + gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux, + stream, src_value); + + if (-1 == index) + return FALSE; + + *dest_value = + gst_util_uint64_scale (stream->samples[index].timestamp, + GST_SECOND, stream->timescale); + GST_DEBUG_OBJECT (qtdemux, "Format Conversion Offset->Time :%" + G_GUINT64_FORMAT "->%" GST_TIME_FORMAT, + src_value, GST_TIME_ARGS (*dest_value)); break; + } default: res = FALSE; break; @@ -611,6 +632,9 @@ gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value, res = FALSE; } +done: + gst_object_unref (qtdemux); + return res; } #endif @@ -621,6 +645,8 @@ gst_qtdemux_get_src_query_types (GstPad * pad) static const GstQueryType src_types[] = { GST_QUERY_POSITION, GST_QUERY_DURATION, + GST_QUERY_CONVERT, + GST_QUERY_FORMATS, GST_QUERY_SEEKING, 0 }; @@ -675,6 +701,24 @@ gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query) } break; } + case GST_QUERY_CONVERT:{ + GstFormat src_fmt, dest_fmt; + gint64 src_value, dest_value = 0; + + gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL); + + res = gst_qtdemux_src_convert (pad, + src_fmt, src_value, dest_fmt, &dest_value); + if (res) { + gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value); + res = TRUE; + } + break; + } + case GST_QUERY_FORMATS: + gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES); + res = TRUE; + break; case GST_QUERY_SEEKING:{ GstFormat fmt; gboolean seekable; @@ -811,7 +855,8 @@ gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str, guint32 index; /* convert media_time to mov format */ - media_time = gst_util_uint64_scale (media_time, str->timescale, GST_SECOND); + media_time = + gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND); result = gst_util_array_binary_search (str->samples, str->stbl_index + 1, sizeof (QtDemuxSample), (GCompareDataFunc) find_func, @@ -825,6 +870,47 @@ gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str, return index; } + + +/* find the index of the sample that includes the data for @media_offset using a + * linear search + * + * Returns the index of the sample. + */ +static guint32 +gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux, + QtDemuxStream * str, gint64 media_offset) +{ + QtDemuxSample *result = str->samples; + guint32 index = 0; + + if (result == NULL || str->n_samples == 0) + return -1; + + if (media_offset == result->offset) + return index; + + result++; + while (index < str->n_samples - 1) { + if (!qtdemux_parse_samples (qtdemux, str, index + 1)) + goto parse_failed; + + if (media_offset < result->offset) + break; + + index++; + result++; + } + return index; + + /* ERRORS */ +parse_failed: + { + GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1); + return -1; + } +} + /* find the index of the sample that includes the data for @media_time using a * linear search, and keeping in mind that not all samples may have been parsed * yet. If possible, it will delegate to binary search. @@ -968,7 +1054,7 @@ gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str, /* position changed, we have a discont */ str->sample_index = index; - /* Each time we move in the stream we store the position where we are + /* Each time we move in the stream we store the position where we are * starting from */ str->from_sample = index; str->discont = TRUE; @@ -1411,6 +1497,7 @@ gst_qtdemux_handle_src_event (GstPad * pad, GstEvent * event) gst_object_unref (qtdemux); +done: return res; /* ERRORS */ @@ -1418,7 +1505,8 @@ index_failed: { GST_ERROR_OBJECT (qtdemux, "Index failed"); gst_event_unref (event); - return FALSE; + res = FALSE; + goto done; } } @@ -1653,7 +1741,8 @@ gst_qtdemux_set_index (GstElement * element, GstIndex * index) /* object lock might be taken again */ if (index) gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id); - GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT, demux->element_index); + GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d", + demux->element_index, demux->index_id); } static GstIndex * @@ -1856,9 +1945,10 @@ qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length) } } +/* caller verifies at least 8 bytes in buf */ static void -extract_initial_length_and_fourcc (const guint8 * data, guint64 * plength, - guint32 * pfourcc) +extract_initial_length_and_fourcc (const guint8 * data, guint size, + guint64 * plength, guint32 * pfourcc) { guint64 length; guint32 fourcc; @@ -1870,7 +1960,7 @@ extract_initial_length_and_fourcc (const guint8 * data, guint64 * plength, if (length == 0) { length = G_MAXUINT32; - } else if (length == 1) { + } else if (length == 1 && size >= 16) { /* this means we have an extended size, which is the 64 bit value of * the next 8 bytes */ length = QT_UINT64 (data + 8); @@ -1886,8 +1976,8 @@ extract_initial_length_and_fourcc (const guint8 * data, guint64 * plength, static gboolean qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br) { - guint32 version; - guint64 duration; + guint32 version = 0; + guint64 duration = 0; if (!gst_byte_reader_get_uint32_be (br, &version)) goto failed; @@ -1897,7 +1987,7 @@ qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br) if (!gst_byte_reader_get_uint64_be (br, &duration)) goto failed; } else { - guint32 dur; + guint32 dur = 0; if (!gst_byte_reader_get_uint32_be (br, &dur)) goto failed; @@ -1929,7 +2019,7 @@ qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream, trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex, &trex_data); while (trex) { - guint32 id, dur, size, flags; + guint32 id = 0, dur = 0, size = 0, flags = 0; /* skip version/flags */ if (!gst_byte_reader_skip (&trex_data, 4)) @@ -1983,11 +2073,11 @@ static gboolean qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun, QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size, guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length, - gint64 * base_offset) + gint64 * base_offset, gint64 * running_offset) { guint64 timestamp; gint32 data_offset = 0; - guint32 flags, first_flags = 0, samples_count; + guint32 flags = 0, first_flags = 0, samples_count = 0; gint i; guint8 *data; guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0; @@ -2016,7 +2106,7 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun, GST_LOG_OBJECT (qtdemux, "base_offset at moof"); *base_offset = moof_offset; } - *base_offset += data_offset; + *running_offset = *base_offset + data_offset; } else { /* if no offset at all, that would mean data starts at moof start, * which is a bit wrong and is ismv crappy way, so compensate @@ -2026,10 +2116,12 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun, GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof"); ismv = TRUE; } + if (*running_offset == -1) + *running_offset = *base_offset; } - GST_LOG_OBJECT (qtdemux, "base offset now %" G_GINT64_FORMAT, *base_offset); - + GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT, + *running_offset); GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d", data_offset, flags, samples_count); @@ -2078,8 +2170,9 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun, QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) goto index_too_big; - GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u (%lu MB)", - stream->n_samples, (stream->n_samples * sizeof (QtDemuxSample)) >> 20); + GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)", + stream->n_samples, (guint) sizeof (QtDemuxSample), + stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0)); /* create a new array of samples if it's the first sample parsed */ if (stream->n_samples == 0) @@ -2135,7 +2228,7 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun, data += entry_size; /* fill the sample information */ - sample->offset = *base_offset; + sample->offset = *running_offset; sample->pts_offset = ct; sample->size = size; sample->timestamp = timestamp; @@ -2144,7 +2237,7 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun, /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe, * now idea how it relates to bitfield other than massive LE/BE confusion */ sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000); - *base_offset += size; + *running_offset += size; timestamp += dur; sample++; } @@ -2209,8 +2302,8 @@ qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd, guint32 * default_sample_size, guint32 * default_sample_flags, gint64 * base_offset) { - guint32 flags; - guint32 track_id; + guint32 flags = 0; + guint32 track_id = 0; if (!gst_byte_reader_skip (tfhd, 1) || !gst_byte_reader_get_uint24_be (tfhd, &flags)) @@ -2220,10 +2313,8 @@ qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd, goto invalid_track; *stream = qtdemux_find_stream (qtdemux, track_id); - if (G_UNLIKELY (!*stream)) { - GST_DEBUG_OBJECT (qtdemux, "unknown stream in tfhd"); - goto invalid_track; - } + if (G_UNLIKELY (!*stream)) + goto unknown_stream; if (flags & TF_BASE_DATA_OFFSET) if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset)) @@ -2254,9 +2345,14 @@ qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd, invalid_track: { - GST_WARNING_OBJECT (qtdemux, "invalid track header"); + GST_WARNING_OBJECT (qtdemux, "invalid track fragment header"); return FALSE; } +unknown_stream: + { + GST_DEBUG_OBJECT (qtdemux, "unknown stream in tfhd"); + return TRUE; + } } static gboolean @@ -2266,7 +2362,7 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length, GNode *moof_node, *traf_node, *tfhd_node, *trun_node; GstByteReader trun_data, tfhd_data; guint32 ds_size = 0, ds_duration = 0, ds_flags = 0; - gint64 base_offset; + gint64 base_offset, running_offset; /* NOTE @stream ignored */ @@ -2275,7 +2371,7 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length, qtdemux_node_dump (qtdemux, moof_node); /* unknown base_offset to start with */ - base_offset = -1; + base_offset = running_offset = -1; traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf); while (traf_node) { /* Fragment Header node */ @@ -2287,18 +2383,31 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length, if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration, &ds_size, &ds_flags, &base_offset)) goto missing_tfhd; + if (G_UNLIKELY (!stream)) { + /* we lost track of offset, we'll need to regain it, + * but can delay complaining until later or avoid doing so altogether */ + base_offset = -2; + goto next; + } + if (G_UNLIKELY (base_offset < -1)) + goto lost_offset; /* Track Run node */ trun_node = qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun, &trun_data); while (trun_node) { qtdemux_parse_trun (qtdemux, &trun_data, stream, - ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset); + ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset, + &running_offset); /* iterate all siblings */ trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun, &trun_data); } - + /* if no new base_offset provided for next traf, + * base is end of current traf */ + base_offset = running_offset; + running_offset = -1; + next: /* iterate all siblings */ traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf); } @@ -2306,11 +2415,20 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length, return TRUE; missing_tfhd: + { + GST_DEBUG_OBJECT (qtdemux, "missing tfhd box"); + goto fail; + } +lost_offset: + { + GST_DEBUG_OBJECT (qtdemux, "lost offset"); + goto fail; + } +fail: { g_node_destroy (moof_node); GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX, - (_("This file is corrupt and cannot be played.")), - ("missing tfhd box")); + (_("This file is corrupt and cannot be played.")), (NULL)); return FALSE; } } @@ -2510,16 +2628,18 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux) ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto beach; - if (G_LIKELY (GST_BUFFER_SIZE (buf) == 16)) - extract_initial_length_and_fourcc (GST_BUFFER_DATA (buf), &length, &fourcc); + if (G_LIKELY (GST_BUFFER_SIZE (buf) >= 8)) + extract_initial_length_and_fourcc (GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf), &length, &fourcc); gst_buffer_unref (buf); + /* maybe we already got most we needed, so only consider this eof */ if (G_UNLIKELY (length == 0)) { - GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX, - (_("This file is invalid and cannot be played.")), + GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX, + (_("Invalid atom size.")), ("Header atom '%" GST_FOURCC_FORMAT "' has empty length", GST_FOURCC_ARGS (fourcc))); - ret = GST_FLOW_ERROR; + ret = GST_FLOW_UNEXPECTED; goto beach; } @@ -2655,7 +2775,7 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux) beach: if (ret == GST_FLOW_UNEXPECTED && qtdemux->got_moov) { /* digested all data, show what we have */ - qtdemux_expose_streams (qtdemux); + ret = qtdemux_expose_streams (qtdemux); /* Only post, event on pads is done after newsegment */ qtdemux_post_global_tags (qtdemux); @@ -2663,13 +2783,13 @@ beach: qtdemux->state = QTDEMUX_STATE_MOVIE; GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)", qtdemux->state); - return GST_FLOW_OK; + return ret; } return ret; } -/* Seeks to the previous keyframe of the indexed stream and - * aligns other streams with respect to the keyframe timestamp +/* Seeks to the previous keyframe of the indexed stream and + * aligns other streams with respect to the keyframe timestamp * of indexed stream. Only called in case of Reverse Playback */ static GstFlowReturn @@ -2684,7 +2804,7 @@ gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux) guint64 seg_media_start_mov; /* segment media start time in mov format */ /* Now we choose an arbitrary stream, get the previous keyframe timestamp - * and finally align all the other streams on that timestamp with their + * and finally align all the other streams on that timestamp with their * respective keyframes */ for (n = 0; n < qtdemux->n_streams; n++) { QtDemuxStream *str = qtdemux->streams[n]; @@ -2892,7 +3012,7 @@ gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream, /* qtdemux->segment.stop is in outside-time-realm, whereas * segment->media_stop is in track-time-realm. - * + * * In order to compare the two, we need to bring segment.stop * into the track-time-realm */ @@ -2909,9 +3029,16 @@ gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream, start = MIN (segment->media_start + seg_time, stop); time = offset; } else { - start = segment->media_start; + if (segment->media_start >= qtdemux->segment.start) { + start = segment->media_start; + time = segment->time; + } else { + start = qtdemux->segment.start; + time = segment->time + (qtdemux->segment.start - segment->media_start); + } + + start = MAX (segment->media_start, qtdemux->segment.start); stop = MIN (segment->media_start + seg_time, stop); - time = segment->time; } GST_DEBUG_OBJECT (qtdemux, "newsegment %d from %" GST_TIME_FORMAT @@ -3202,7 +3329,7 @@ gst_qtdemux_sync_streams (GstQTDemux * demux) } /* UNEXPECTED and NOT_LINKED need to be combined. This means that we return: - * + * * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED. * GST_FLOW_UNEXPECTED: when all pads UNEXPECTED or NOT_LINKED. */ @@ -3218,6 +3345,11 @@ gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream, /* store the value */ stream->last_ret = ret; + /* any other error that is not-linked or eos can be returned right away */ + if (G_LIKELY (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_NOT_LINKED)) + goto done; + + /* only return NOT_LINKED if all other pads returned NOT_LINKED */ for (i = 0; i < demux->n_streams; i++) { QtDemuxStream *ostream = demux->streams[i]; @@ -3492,7 +3624,7 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux, gst_index_add_association (qtdemux->element_index, qtdemux->index_id, keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT : - GST_ASSOCIATION_FLAG_NONE, GST_FORMAT_TIME, stream_time, + GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time, GST_FORMAT_BYTES, byte_position, NULL); } } @@ -3697,7 +3829,7 @@ pause: gint64 stop; /* FIXME: I am not sure this is the right fix. If the sinks are - * supposed to detect the segment is complete and accumulate + * supposed to detect the segment is complete and accumulate * automatically, it does not seem to work here. Need more work */ qtdemux->segment_running = TRUE; @@ -3860,7 +3992,8 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) data = gst_adapter_peek (demux->adapter, demux->neededbytes); /* get fourcc/length, set neededbytes */ - extract_initial_length_and_fourcc ((guint8 *) data, &size, &fourcc); + extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes, + &size, &fourcc); GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] " "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size); if (size == 0) { @@ -3951,7 +4084,8 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) data = gst_adapter_peek (demux->adapter, demux->neededbytes); /* parse the header */ - extract_initial_length_and_fourcc (data, NULL, &fourcc); + extract_initial_length_and_fourcc (data, demux->neededbytes, NULL, + &fourcc); if (fourcc == FOURCC_moov) { GST_DEBUG_OBJECT (demux, "Parsing [moov]"); @@ -3976,8 +4110,10 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) if (demux->got_moov && demux->fragmented) { GST_DEBUG_OBJECT (demux, "Parsing [moof]"); if (!qtdemux_parse_moof (demux, data, demux->neededbytes, - demux->offset, NULL)) - return GST_FLOW_ERROR; + demux->offset, NULL)) { + ret = GST_FLOW_ERROR; + goto done; + } } else { GST_DEBUG_OBJECT (demux, "Discarding [moof]"); } @@ -4566,12 +4702,15 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer, } case FOURCC_mp4v: case FOURCC_MP4V: + case FOURCC_fmp4: + case FOURCC_FMP4: { const guint8 *buf; guint32 version; int tlen; - GST_DEBUG_OBJECT (qtdemux, "parsing in mp4v"); + GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (fourcc)); version = QT_UINT32 (buffer + 16); GST_DEBUG_OBJECT (qtdemux, "version %08x", version); if (1 || version == 0x00000000) { @@ -4950,11 +5089,13 @@ qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset, if (G_UNLIKELY (ret != GST_FLOW_OK)) goto locate_failed; if (G_LIKELY (GST_BUFFER_SIZE (buf) != 16)) { + /* likely EOF */ + ret = GST_FLOW_UNEXPECTED; gst_buffer_unref (buf); - ret = GST_FLOW_ERROR; goto locate_failed; } - extract_initial_length_and_fourcc (GST_BUFFER_DATA (buf), length, &lfourcc); + extract_initial_length_and_fourcc (GST_BUFFER_DATA (buf), 16, length, + &lfourcc); gst_buffer_unref (buf); if (G_UNLIKELY (*length == 0)) { @@ -4987,20 +5128,20 @@ locate_failed: /* should only do something in pull mode */ /* call with OBJECT lock */ -static gboolean +static GstFlowReturn qtdemux_add_fragmented_samples (GstQTDemux * qtdemux) { guint64 length, offset; GstBuffer *buf = NULL; GstFlowReturn ret = GST_FLOW_OK; - gboolean res = TRUE; + GstFlowReturn res = TRUE; offset = qtdemux->moof_offset; GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset); if (!offset) { GST_DEBUG_OBJECT (qtdemux, "no next moof"); - return FALSE; + return GST_FLOW_UNEXPECTED; } /* best not do pull etc with lock held */ @@ -5040,7 +5181,7 @@ parse_failed: { GST_DEBUG_OBJECT (qtdemux, "failed to parse moof"); offset = 0; - res = FALSE; + res = GST_FLOW_ERROR; goto exit; } flow_failed: @@ -5053,7 +5194,7 @@ flow_failed: GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE"); /* resume at current position next time */ } - res = FALSE; + res = ret; goto exit; } } @@ -5199,10 +5340,9 @@ qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl) goto corrupt_file; } - GST_DEBUG_OBJECT (qtdemux, - "allocating n_samples %u * %" G_GSIZE_FORMAT " = (%u MB)", - stream->n_samples, sizeof (QtDemuxSample), - (guint) (stream->n_samples * sizeof (QtDemuxSample)) >> 20); + GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)", + stream->n_samples, (guint) sizeof (QtDemuxSample), + stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0)); if (stream->n_samples >= QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) { @@ -5644,7 +5784,7 @@ done: GST_DEBUG_OBJECT (qtdemux, "parsed all available samples; checking for more"); while (n + 1 == stream->n_samples) - if (!qtdemux_add_fragmented_samples (qtdemux)) + if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK) break; } GST_OBJECT_UNLOCK (qtdemux); @@ -5929,7 +6069,7 @@ qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf) GST_DEBUG_OBJECT (qtdemux, "Found hndl atom"); /* skip data reference handle bytes and the - * following pascal string and some extra 4 + * following pascal string and some extra 4 * bytes I have no idea what are */ if (!gst_byte_reader_skip (&dref, 4) || !gst_byte_reader_get_uint8 (&dref, &string_len) || @@ -6204,13 +6344,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) esds = NULL; pasp = NULL; - mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4v); - if (!mp4v) - mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_MP4V); - /* H264 is MPEG-4 after all, - * and qt seems to put MPEG-4 stuff in there as well */ - if (!mp4v) - mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_avc1); + /* pick 'the' stsd child */ + mp4v = qtdemux_tree_get_child_by_type (stsd, fourcc); if (mp4v) { esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds); pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp); @@ -6320,13 +6455,17 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) } case FOURCC_mp4v: case FOURCC_MP4V: + case FOURCC_fmp4: + case FOURCC_FMP4: { GNode *glbl; - GST_DEBUG_OBJECT (qtdemux, "found mp4v"); + GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (fourcc)); /* codec data might be in glbl extension atom */ - glbl = qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl); + glbl = mp4v ? + qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL; if (glbl) { guint8 *data; GstBuffer *buf; @@ -6344,6 +6483,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) gst_buffer_unref (buf); } } + break; } case FOURCC_mjp2: { @@ -6887,31 +7027,34 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) guint32 headerlen; waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc); - waveheader = (const guint8 *) waveheadernode->data; - headerlen = QT_UINT32 (waveheader); + if (waveheadernode) { + waveheader = (const guint8 *) waveheadernode->data; + headerlen = QT_UINT32 (waveheader); - if (headerlen > 8) { - gst_riff_strf_auds *header = NULL; - GstBuffer *headerbuf; - GstBuffer *extra; + if (headerlen > 8) { + gst_riff_strf_auds *header = NULL; + GstBuffer *headerbuf; + GstBuffer *extra; - waveheader += 8; - headerlen -= 8; + waveheader += 8; + headerlen -= 8; - headerbuf = gst_buffer_new (); - GST_BUFFER_DATA (headerbuf) = (guint8 *) waveheader; - GST_BUFFER_SIZE (headerbuf) = headerlen; + headerbuf = gst_buffer_new (); + GST_BUFFER_DATA (headerbuf) = (guint8 *) waveheader; + GST_BUFFER_SIZE (headerbuf) = headerlen; - if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux), - headerbuf, &header, &extra)) { - gst_caps_unref (stream->caps); - stream->caps = gst_riff_create_audio_caps (header->format, NULL, - header, extra, NULL, NULL); + if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux), + headerbuf, &header, &extra)) { + gst_caps_unref (stream->caps); + stream->caps = gst_riff_create_audio_caps (header->format, NULL, + header, extra, NULL, NULL); - if (extra) - gst_buffer_unref (extra); + if (extra) + gst_buffer_unref (extra); + } } - } + } else + GST_DEBUG ("Didn't find waveheadernode for this codec"); } g_node_destroy (wavenode); } @@ -7192,14 +7335,15 @@ too_many_streams: } } -static void +static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux) { gint i; + GstFlowReturn ret = GST_FLOW_OK; GST_DEBUG_OBJECT (qtdemux, "exposing streams"); - for (i = 0; i < qtdemux->n_streams; i++) { + for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) { QtDemuxStream *stream = qtdemux->streams[i]; guint32 sample_num = 0; guint samples = 20; @@ -7213,7 +7357,7 @@ qtdemux_expose_streams (GstQTDemux * qtdemux) /* need all moov samples first */ GST_OBJECT_LOCK (qtdemux); while (stream->n_samples == 0) - if (!qtdemux_add_fragmented_samples (qtdemux)) + if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK) break; GST_OBJECT_UNLOCK (qtdemux); } else { @@ -7221,6 +7365,10 @@ qtdemux_expose_streams (GstQTDemux * qtdemux) qtdemux->moof_offset = 0; } + /* prepare braking */ + if (ret != GST_FLOW_ERROR) + ret = GST_FLOW_OK; + /* in pull mode, we should have parsed some sample info by now; * and quite some code will not handle no samples. * in push mode, we'll just have to deal with it */ @@ -7231,6 +7379,7 @@ qtdemux_expose_streams (GstQTDemux * qtdemux) sizeof (QtDemuxStream *) * (GST_QTDEMUX_MAX_STREAMS - i - 1)); qtdemux->streams[GST_QTDEMUX_MAX_STREAMS - 1] = NULL; qtdemux->n_streams--; + i--; continue; } @@ -7279,6 +7428,8 @@ qtdemux_expose_streams (GstQTDemux * qtdemux) gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m); qtdemux->posted_redirect = TRUE; } + + return ret; } /* check if major or compatible brand is 3GP */ @@ -7882,26 +8033,42 @@ qtdemux_tag_add_revdns (GstQTDemux * demux, const char *tag, datatype = QT_UINT32 (((gchar *) data->data) + 8) & 0xFFFFFF; if (strncmp (meanstr, "com.apple.iTunes", meansize - 12) == 0) { - if (strncmp (namestr, "replaygain_track_gain", namesize - 12) == 0) { - qtdemux_add_double_tag_from_str (demux, - GST_TAG_TRACK_GAIN, ((guint8 *) data->data) + 16, datasize - 16); + static const struct + { + const gchar name[28]; + const gchar tag[28]; + } tags[] = { + { + "replaygain_track_gain", GST_TAG_TRACK_GAIN}, { + "replaygain_track_peak", GST_TAG_TRACK_PEAK}, { + "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, { + "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, { + "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, { + "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, { + "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, { + "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID} + }; + int i; - } else if (strncmp (namestr, "replaygain_track_peak", namesize - 12) == 0) { - qtdemux_add_double_tag_from_str (demux, - GST_TAG_TRACK_PEAK, ((guint8 *) data->data) + 16, datasize - 16); - - } else if (strncmp (namestr, "replaygain_album_gain", namesize - 12) == 0) { - qtdemux_add_double_tag_from_str (demux, - GST_TAG_ALBUM_GAIN, ((guint8 *) data->data) + 16, datasize - 16); - - } else if (strncmp (namestr, "replaygain_album_peak", namesize - 12) == 0) { - qtdemux_add_double_tag_from_str (demux, - GST_TAG_ALBUM_PEAK, ((guint8 *) data->data) + 16, datasize - 16); - - } else { - goto unknown_tag; + for (i = 0; i < G_N_ELEMENTS (tags); ++i) { + if (!g_ascii_strncasecmp (tags[i].name, namestr, namesize - 12)) { + switch (gst_tag_get_type (tags[i].tag)) { + case G_TYPE_DOUBLE: + qtdemux_add_double_tag_from_str (demux, tags[i].tag, + ((guint8 *) data->data) + 16, datasize - 16); + break; + case G_TYPE_STRING: + qtdemux_tag_add_str (demux, tags[i].tag, NULL, node); + break; + default: + /* not reached */ + break; + } + break; + } } - + if (i == G_N_ELEMENTS (tags)) + goto unknown_tag; } else { goto unknown_tag; } @@ -8756,6 +8923,7 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, NULL); break; case GST_MAKE_FOURCC ('2', 'v', 'u', 'y'): + case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'): _codec ("Raw packed YUV 4:2:2"); caps = gst_caps_new_simple ("video/x-raw-yuv", "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'), @@ -8767,6 +8935,16 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('v', '2', '1', '0'), NULL); break; + case GST_MAKE_FOURCC ('r', '2', '1', '0'): + _codec ("Raw packed RGB 10-bit 4:4:4"); + caps = gst_caps_new_simple ("video/x-raw-rgb", + "endianness", G_TYPE_INT, G_BIG_ENDIAN, "depth", G_TYPE_INT, 30, + "bpp", G_TYPE_INT, 32, + "endianness", G_TYPE_INT, G_BIG_ENDIAN, + "red_mask", G_TYPE_INT, 0x3ff00000, + "green_mask", G_TYPE_INT, 0x000ffc00, + "blue_mask", G_TYPE_INT, 0x000003ff, NULL); + break; case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'): case GST_MAKE_FOURCC ('m', 'p', 'g', '1'): _codec ("MPEG-1 video"); diff --git a/gst/qtdemux/qtdemux_dump.c b/gst/qtdemux/qtdemux_dump.c index b384fcd4dc..fa66767583 100644 --- a/gst/qtdemux/qtdemux_dump.c +++ b/gst/qtdemux/qtdemux_dump.c @@ -523,7 +523,7 @@ gboolean qtdemux_dump_tfra (GstQTDemux * qtdemux, GstByteReader * data, int depth) { guint64 time = 0, moof_offset = 0; - guint32 len = 0, num_entries = 0, ver_flags, track_id, i; + guint32 len = 0, num_entries = 0, ver_flags = 0, track_id = 0, i; guint value_size, traf_size, trun_size, sample_size; if (!gst_byte_reader_get_uint32_be (data, &ver_flags)) @@ -569,8 +569,8 @@ qtdemux_dump_tfra (GstQTDemux * qtdemux, GstByteReader * data, int depth) gboolean qtdemux_dump_tfhd (GstQTDemux * qtdemux, GstByteReader * data, int depth) { - guint32 flags, n, track_id; - guint64 base_data_offset; + guint32 flags = 0, n = 0, track_id = 0; + guint64 base_data_offset = 0; if (!gst_byte_reader_skip (data, 1) || !gst_byte_reader_get_uint24_be (data, &flags)) @@ -621,8 +621,9 @@ qtdemux_dump_tfhd (GstQTDemux * qtdemux, GstByteReader * data, int depth) gboolean qtdemux_dump_trun (GstQTDemux * qtdemux, GstByteReader * data, int depth) { - guint32 flags, samples_count, data_offset, first_sample_flags; - guint32 sample_duration, sample_size, sample_flags, composition_time_offsets; + guint32 flags = 0, samples_count = 0, data_offset = 0, first_sample_flags = 0; + guint32 sample_duration = 0, sample_size = 0, sample_flags = + 0, composition_time_offsets = 0; int i = 0; if (!gst_byte_reader_skip (data, 1) || @@ -700,7 +701,7 @@ qtdemux_dump_trex (GstQTDemux * qtdemux, GstByteReader * data, int depth) gboolean qtdemux_dump_mehd (GstQTDemux * qtdemux, GstByteReader * data, int depth) { - guint32 version; + guint32 version = 0; guint64 fragment_duration; guint value_size; diff --git a/gst/qtdemux/qtdemux_fourcc.h b/gst/qtdemux/qtdemux_fourcc.h index d7875cc668..29ad155a3f 100644 --- a/gst/qtdemux/qtdemux_fourcc.h +++ b/gst/qtdemux/qtdemux_fourcc.h @@ -75,6 +75,8 @@ G_BEGIN_DECLS #define FOURCC_mp4a GST_MAKE_FOURCC('m','p','4','a') #define FOURCC_mp4v GST_MAKE_FOURCC('m','p','4','v') #define FOURCC_MP4V GST_MAKE_FOURCC('M','P','4','V') +#define FOURCC_fmp4 GST_MAKE_FOURCC('f','m','p','4') +#define FOURCC_FMP4 GST_MAKE_FOURCC('F','M','P','4') #define FOURCC_glbl GST_MAKE_FOURCC('g','l','b','l') #define FOURCC_wave GST_MAKE_FOURCC('w','a','v','e') #define FOURCC_appl GST_MAKE_FOURCC('a','p','p','l') diff --git a/gst/rtp/Makefile.am b/gst/rtp/Makefile.am index 5a300b33c6..82ccd5fedb 100644 --- a/gst/rtp/Makefile.am +++ b/gst/rtp/Makefile.am @@ -6,12 +6,15 @@ libgstrtp_la_SOURCES = \ gstrtpchannels.c \ gstrtpdepay.c \ gstrtpac3depay.c \ + gstrtpac3pay.c \ gstrtpbvdepay.c \ gstrtpbvpay.c \ gstrtpceltdepay.c \ gstrtpceltpay.c \ gstrtpdvdepay.c \ gstrtpdvpay.c \ + gstrtpgstdepay.c \ + gstrtpgstpay.c \ gstrtpilbcdepay.c \ gstrtpilbcpay.c \ gstrtpmpadepay.c \ @@ -94,6 +97,7 @@ noinst_HEADERS = \ gstrtpL16depay.h \ gstrtpL16pay.h \ gstrtpac3depay.h \ + gstrtpac3pay.h \ gstrtpbvdepay.h \ gstrtpbvpay.h \ gstrtpceltpay.h \ @@ -102,6 +106,8 @@ noinst_HEADERS = \ gstrtpdvpay.h \ gstrtpamrdepay.h \ gstrtpamrpay.h \ + gstrtpgstdepay.h \ + gstrtpgstpay.h \ gstrtpilbcdepay.h \ gstrtpilbcpay.h \ gstrtppcmadepay.h \ diff --git a/gst/rtp/gstasteriskh263.c b/gst/rtp/gstasteriskh263.c index a1ad4dee4f..79aa32be44 100644 --- a/gst/rtp/gstasteriskh263.c +++ b/gst/rtp/gstasteriskh263.c @@ -85,7 +85,7 @@ gst_asteriskh263_base_init (gpointer klass) gst_static_pad_template_get (&gst_asteriskh263_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP Asterisk H263 depayloader", "Codec/Depayloader/Network", + "RTP Asterisk H263 depayloader", "Codec/Depayloader/Network/RTP", "Extracts H263 video from RTP and encodes in Asterisk H263 format", "Neil Stratford "); } @@ -231,5 +231,5 @@ gboolean gst_asteriskh263_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "asteriskh263", - GST_RANK_MARGINAL, GST_TYPE_ASTERISK_H263); + GST_RANK_SECONDARY, GST_TYPE_ASTERISK_H263); } diff --git a/gst/rtp/gstrtp.c b/gst/rtp/gstrtp.c index 339ff97470..721cee3c84 100644 --- a/gst/rtp/gstrtp.c +++ b/gst/rtp/gstrtp.c @@ -23,12 +23,15 @@ #include "gstrtpdepay.h" #include "gstrtpac3depay.h" +#include "gstrtpac3pay.h" #include "gstrtpbvdepay.h" #include "gstrtpbvpay.h" #include "gstrtpceltdepay.h" #include "gstrtpceltpay.h" #include "gstrtpdvdepay.h" #include "gstrtpdvpay.h" +#include "gstrtpgstdepay.h" +#include "gstrtpgstpay.h" #include "gstrtpilbcdepay.h" #include "gstrtpilbcpay.h" #include "gstrtppcmupay.h" @@ -97,6 +100,9 @@ plugin_init (GstPlugin * plugin) if (!gst_rtp_ac3_depay_plugin_init (plugin)) return FALSE; + if (!gst_rtp_ac3_pay_plugin_init (plugin)) + return FALSE; + if (!gst_rtp_bv_depay_plugin_init (plugin)) return FALSE; @@ -115,6 +121,12 @@ plugin_init (GstPlugin * plugin) if (!gst_rtp_dv_pay_plugin_init (plugin)) return FALSE; + if (!gst_rtp_gst_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_gst_pay_plugin_init (plugin)) + return FALSE; + if (!gst_rtp_ilbc_pay_plugin_init (plugin)) return FALSE; diff --git a/gst/rtp/gstrtpL16depay.c b/gst/rtp/gstrtpL16depay.c index ac05d8b2fe..7943f1b469 100644 --- a/gst/rtp/gstrtpL16depay.c +++ b/gst/rtp/gstrtpL16depay.c @@ -86,7 +86,7 @@ gst_rtp_L16_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_L16_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP audio depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts raw audio from RTP packets", "Zeeshan Ali ," "Wim Taymans "); } @@ -261,5 +261,5 @@ gboolean gst_rtp_L16_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpL16depay", - GST_RANK_MARGINAL, GST_TYPE_RTP_L16_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_L16_DEPAY); } diff --git a/gst/rtp/gstrtpL16pay.c b/gst/rtp/gstrtpL16pay.c index 6d5ab352d2..a6fbacc4b0 100644 --- a/gst/rtp/gstrtpL16pay.c +++ b/gst/rtp/gstrtpL16pay.c @@ -86,7 +86,7 @@ gst_rtp_L16_pay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_L16_pay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP audio payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payload-encode Raw audio into RTP packets (RFC 3551)", "Wim Taymans "); } @@ -234,5 +234,5 @@ gboolean gst_rtp_L16_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpL16pay", - GST_RANK_NONE, GST_TYPE_RTP_L16_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_L16_PAY); } diff --git a/gst/rtp/gstrtpac3depay.c b/gst/rtp/gstrtpac3depay.c index c1136c1f4f..2ba4cefe77 100644 --- a/gst/rtp/gstrtpac3depay.c +++ b/gst/rtp/gstrtpac3depay.c @@ -66,7 +66,7 @@ gst_rtp_ac3_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_ac3_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP AC3 depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts AC3 audio from RTP packets (RFC 4184)", "Wim Taymans "); } @@ -82,7 +82,7 @@ gst_rtp_ac3_depay_class_init (GstRtpAC3DepayClass * klass) gstbasertpdepayload_class->process = gst_rtp_ac3_depay_process; GST_DEBUG_CATEGORY_INIT (rtpac3depay_debug, "rtpac3depay", 0, - "MPEG Audio RTP Depayloader"); + "AC3 Audio RTP Depayloader"); } static void @@ -214,5 +214,5 @@ gboolean gst_rtp_ac3_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpac3depay", - GST_RANK_MARGINAL, GST_TYPE_RTP_AC3_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_AC3_DEPAY); } diff --git a/gst/rtp/gstrtpac3pay.c b/gst/rtp/gstrtpac3pay.c new file mode 100644 index 0000000000..fddea38472 --- /dev/null +++ b/gst/rtp/gstrtpac3pay.c @@ -0,0 +1,452 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include + +#include "gstrtpac3pay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpac3pay_debug); +#define GST_CAT_DEFAULT (rtpac3pay_debug) + +static GstStaticPadTemplate gst_rtp_ac3_pay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/ac3; " "audio/x-ac3; ") + ); + +static GstStaticPadTemplate gst_rtp_ac3_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) { 32000, 44100, 48000 }, " + "encoding-name = (string) \"AC3\"") + ); + +static void gst_rtp_ac3_pay_finalize (GObject * object); + +static GstStateChangeReturn gst_rtp_ac3_pay_change_state (GstElement * element, + GstStateChange transition); + +static gboolean gst_rtp_ac3_pay_setcaps (GstBaseRTPPayload * payload, + GstCaps * caps); +static gboolean gst_rtp_ac3_pay_handle_event (GstPad * pad, GstEvent * event); +static GstFlowReturn gst_rtp_ac3_pay_flush (GstRtpAC3Pay * rtpac3pay); +static GstFlowReturn gst_rtp_ac3_pay_handle_buffer (GstBaseRTPPayload * payload, + GstBuffer * buffer); + +GST_BOILERPLATE (GstRtpAC3Pay, gst_rtp_ac3_pay, GstBaseRTPPayload, + GST_TYPE_BASE_RTP_PAYLOAD) + + static void gst_rtp_ac3_pay_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_ac3_pay_src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_ac3_pay_sink_template)); + + gst_element_class_set_details_simple (element_class, + "RTP AC3 audio payloader", "Codec/Payloader/Network/RTP", + "Payload AC3 audio as RTP packets (RFC 4184)", + "Wim Taymans "); +} + +static void +gst_rtp_ac3_pay_class_init (GstRtpAC3PayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseRTPPayloadClass *gstbasertppayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass; + + gobject_class->finalize = gst_rtp_ac3_pay_finalize; + + gstelement_class->change_state = gst_rtp_ac3_pay_change_state; + + gstbasertppayload_class->set_caps = gst_rtp_ac3_pay_setcaps; + gstbasertppayload_class->handle_event = gst_rtp_ac3_pay_handle_event; + gstbasertppayload_class->handle_buffer = gst_rtp_ac3_pay_handle_buffer; + + GST_DEBUG_CATEGORY_INIT (rtpac3pay_debug, "rtpac3pay", 0, + "AC3 Audio RTP Depayloader"); +} + +static void +gst_rtp_ac3_pay_init (GstRtpAC3Pay * rtpac3pay, GstRtpAC3PayClass * klass) +{ + rtpac3pay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_ac3_pay_finalize (GObject * object) +{ + GstRtpAC3Pay *rtpac3pay; + + rtpac3pay = GST_RTP_AC3_PAY (object); + + g_object_unref (rtpac3pay->adapter); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_rtp_ac3_pay_reset (GstRtpAC3Pay * pay) +{ + pay->first_ts = -1; + pay->duration = 0; + gst_adapter_clear (pay->adapter); + GST_DEBUG_OBJECT (pay, "reset depayloader"); +} + +static gboolean +gst_rtp_ac3_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) +{ + gboolean res; + gint rate; + GstStructure *structure; + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "rate", &rate)) + rate = 90000; /* default */ + + gst_basertppayload_set_options (payload, "audio", TRUE, "AC3", rate); + res = gst_basertppayload_set_outcaps (payload, NULL); + + return res; +} + +static gboolean +gst_rtp_ac3_pay_handle_event (GstPad * pad, GstEvent * event) +{ + GstRtpAC3Pay *rtpac3pay; + + rtpac3pay = GST_RTP_AC3_PAY (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* make sure we push the last packets in the adapter on EOS */ + gst_rtp_ac3_pay_flush (rtpac3pay); + break; + case GST_EVENT_FLUSH_STOP: + gst_rtp_ac3_pay_reset (rtpac3pay); + break; + default: + break; + } + + gst_object_unref (rtpac3pay); + + /* FALSE to let the parent handle the event as well */ + return FALSE; +} + +struct frmsize_s +{ + guint16 bit_rate; + guint16 frm_size[3]; +}; + +static const struct frmsize_s frmsizecod_tbl[] = { + {32, {64, 69, 96}}, + {32, {64, 70, 96}}, + {40, {80, 87, 120}}, + {40, {80, 88, 120}}, + {48, {96, 104, 144}}, + {48, {96, 105, 144}}, + {56, {112, 121, 168}}, + {56, {112, 122, 168}}, + {64, {128, 139, 192}}, + {64, {128, 140, 192}}, + {80, {160, 174, 240}}, + {80, {160, 175, 240}}, + {96, {192, 208, 288}}, + {96, {192, 209, 288}}, + {112, {224, 243, 336}}, + {112, {224, 244, 336}}, + {128, {256, 278, 384}}, + {128, {256, 279, 384}}, + {160, {320, 348, 480}}, + {160, {320, 349, 480}}, + {192, {384, 417, 576}}, + {192, {384, 418, 576}}, + {224, {448, 487, 672}}, + {224, {448, 488, 672}}, + {256, {512, 557, 768}}, + {256, {512, 558, 768}}, + {320, {640, 696, 960}}, + {320, {640, 697, 960}}, + {384, {768, 835, 1152}}, + {384, {768, 836, 1152}}, + {448, {896, 975, 1344}}, + {448, {896, 976, 1344}}, + {512, {1024, 1114, 1536}}, + {512, {1024, 1115, 1536}}, + {576, {1152, 1253, 1728}}, + {576, {1152, 1254, 1728}}, + {640, {1280, 1393, 1920}}, + {640, {1280, 1394, 1920}} +}; + +static GstFlowReturn +gst_rtp_ac3_pay_flush (GstRtpAC3Pay * rtpac3pay) +{ + guint avail, FT, NF, mtu; + GstBuffer *outbuf; + GstFlowReturn ret; + + /* the data available in the adapter is either smaller + * than the MTU or bigger. In the case it is smaller, the complete + * adapter contents can be put in one packet. In the case the + * adapter has more than one MTU, we need to split the AC3 data + * over multiple packets. */ + avail = gst_adapter_available (rtpac3pay->adapter); + + ret = GST_FLOW_OK; + + FT = 0; + /* number of frames */ + NF = rtpac3pay->NF; + + mtu = GST_BASE_RTP_PAYLOAD_MTU (rtpac3pay); + + GST_LOG_OBJECT (rtpac3pay, "flushing %u bytes", avail); + + while (avail > 0) { + guint towrite; + guint8 *payload; + guint payload_len; + guint packet_len; + + /* this will be the total length of the packet */ + packet_len = gst_rtp_buffer_calc_packet_len (2 + avail, 0, 0); + + /* fill one MTU or all available bytes */ + towrite = MIN (packet_len, mtu); + + /* this is the payload length */ + payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); + + /* create buffer to hold the payload */ + outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); + + if (FT == 0) { + /* check if it all fits */ + if (towrite < packet_len) { + guint maxlen; + + GST_LOG_OBJECT (rtpac3pay, "we need to fragment"); + /* check if we will be able to put at least 5/8th of the total + * frame in this first frame. */ + if ((avail * 5) / 8 >= (payload_len - 2)) + FT = 1; + else + FT = 2; + /* check how many fragments we will need */ + maxlen = gst_rtp_buffer_calc_payload_len (mtu - 2, 0, 0); + NF = (avail + maxlen - 1) / maxlen; + } + } else if (FT != 3) { + /* remaining fragment */ + FT = 3; + } + + /* + * 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MBZ | FT| NF | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * FT: 0: one or more complete frames + * 1: initial 5/8 fragment + * 2: initial fragment not 5/8 + * 3: other fragment + * NF: amount of frames if FT = 0, else number of fragments. + */ + GST_LOG_OBJECT (rtpac3pay, "FT %u, NF %u", FT, NF); + payload = gst_rtp_buffer_get_payload (outbuf); + payload[0] = (FT & 3); + payload[1] = NF; + payload_len -= 2; + + gst_adapter_copy (rtpac3pay->adapter, &payload[2], 0, payload_len); + gst_adapter_flush (rtpac3pay->adapter, payload_len); + + avail -= payload_len; + if (avail == 0) + gst_rtp_buffer_set_marker (outbuf, TRUE); + + GST_BUFFER_TIMESTAMP (outbuf) = rtpac3pay->first_ts; + GST_BUFFER_DURATION (outbuf) = rtpac3pay->duration; + + ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpac3pay), outbuf); + } + + return ret; +} + +static GstFlowReturn +gst_rtp_ac3_pay_handle_buffer (GstBaseRTPPayload * basepayload, + GstBuffer * buffer) +{ + GstRtpAC3Pay *rtpac3pay; + GstFlowReturn ret; + guint size, avail, left, NF; + guint8 *data, *p; + guint packet_len; + GstClockTime duration, timestamp; + + rtpac3pay = GST_RTP_AC3_PAY (basepayload); + + size = GST_BUFFER_SIZE (buffer); + data = GST_BUFFER_DATA (buffer); + duration = GST_BUFFER_DURATION (buffer); + timestamp = GST_BUFFER_TIMESTAMP (buffer); + + if (GST_BUFFER_IS_DISCONT (buffer)) { + GST_DEBUG_OBJECT (rtpac3pay, "DISCONT"); + gst_rtp_ac3_pay_reset (rtpac3pay); + } + + /* count the amount of incomming packets */ + NF = 0; + left = size; + p = data; + while (TRUE) { + guint bsid, fscod, frmsizecod, frame_size; + + if (left < 6) + break; + + if (p[0] != 0x0b || p[1] != 0x77) + break; + + bsid = p[5] >> 3; + if (bsid > 8) + break; + + frmsizecod = p[4] & 0x3f; + fscod = p[4] >> 6; + + GST_DEBUG_OBJECT (rtpac3pay, "fscod %u, %u", fscod, frmsizecod); + + if (fscod >= 3 || frmsizecod >= 38) + break; + + frame_size = frmsizecod_tbl[frmsizecod].frm_size[fscod] * 2; + if (frame_size > left) + break; + + NF++; + GST_DEBUG_OBJECT (rtpac3pay, "found frame %u of size %u", NF, frame_size); + + p += frame_size; + left -= frame_size; + } + if (NF == 0) + goto no_frames; + + avail = gst_adapter_available (rtpac3pay->adapter); + + /* get packet length of previous data and this new data, + * payload length includes a 4 byte header */ + packet_len = gst_rtp_buffer_calc_packet_len (2 + avail + size, 0, 0); + + /* if this buffer is going to overflow the packet, flush what we + * have. */ + if (gst_basertppayload_is_filled (basepayload, + packet_len, rtpac3pay->duration + duration)) { + ret = gst_rtp_ac3_pay_flush (rtpac3pay); + avail = 0; + } else { + ret = GST_FLOW_OK; + } + + if (avail == 0) { + GST_DEBUG_OBJECT (rtpac3pay, + "first packet, save timestamp %" GST_TIME_FORMAT, + GST_TIME_ARGS (timestamp)); + rtpac3pay->first_ts = timestamp; + rtpac3pay->duration = 0; + rtpac3pay->NF = 0; + } + + gst_adapter_push (rtpac3pay->adapter, buffer); + rtpac3pay->duration += duration; + rtpac3pay->NF += NF; + + return ret; + + /* ERRORS */ +no_frames: + { + GST_WARNING_OBJECT (rtpac3pay, "no valid AC3 frames found"); + return GST_FLOW_OK; + } +} + +static GstStateChangeReturn +gst_rtp_ac3_pay_change_state (GstElement * element, GstStateChange transition) +{ + GstRtpAC3Pay *rtpac3pay; + GstStateChangeReturn ret; + + rtpac3pay = GST_RTP_AC3_PAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_ac3_pay_reset (rtpac3pay); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_ac3_pay_reset (rtpac3pay); + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_ac3_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpac3pay", + GST_RANK_SECONDARY, GST_TYPE_RTP_AC3_PAY); +} diff --git a/gst/rtp/gstrtpac3pay.h b/gst/rtp/gstrtpac3pay.h new file mode 100644 index 0000000000..2ca9d997d2 --- /dev/null +++ b/gst/rtp/gstrtpac3pay.h @@ -0,0 +1,64 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_RTP_AC3_PAY_H__ +#define __GST_RTP_AC3_PAY_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_AC3_PAY \ + (gst_rtp_ac3_pay_get_type()) +#define GST_RTP_AC3_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_AC3_PAY,GstRtpAC3Pay)) +#define GST_RTP_AC3_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_AC3_PAY,GstRtpAC3PayClass)) +#define GST_IS_RTP_AC3_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_AC3_PAY)) +#define GST_IS_RTP_AC3_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_AC3_PAY)) + +typedef struct _GstRtpAC3Pay GstRtpAC3Pay; +typedef struct _GstRtpAC3PayClass GstRtpAC3PayClass; + +struct _GstRtpAC3Pay +{ + GstBaseRTPPayload payload; + + GstAdapter *adapter; + GstClockTime first_ts; + GstClockTime duration; + guint NF; +}; + +struct _GstRtpAC3PayClass +{ + GstBaseRTPPayloadClass parent_class; +}; + +GType gst_rtp_ac3_pay_get_type (void); + +gboolean gst_rtp_ac3_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_AC3_PAY_H__ */ diff --git a/gst/rtp/gstrtpamrdepay.c b/gst/rtp/gstrtpamrdepay.c index c08c406882..f28904bddb 100644 --- a/gst/rtp/gstrtpamrdepay.c +++ b/gst/rtp/gstrtpamrdepay.c @@ -125,7 +125,7 @@ gst_rtp_amr_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_amr_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP AMR depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts AMR or AMR-WB audio from RTP packets (RFC 3267)", "Wim Taymans "); } @@ -455,5 +455,5 @@ gboolean gst_rtp_amr_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpamrdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_AMR_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_AMR_DEPAY); } diff --git a/gst/rtp/gstrtpamrpay.c b/gst/rtp/gstrtpamrpay.c index 57cfb55dfa..89a149fe26 100644 --- a/gst/rtp/gstrtpamrpay.c +++ b/gst/rtp/gstrtpamrpay.c @@ -107,7 +107,7 @@ gst_rtp_amr_pay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_amr_pay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP AMR payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payload-encode AMR or AMR-WB audio into RTP packets (RFC 3267)", "Wim Taymans "); } @@ -435,5 +435,5 @@ gboolean gst_rtp_amr_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpamrpay", - GST_RANK_NONE, GST_TYPE_RTP_AMR_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_AMR_PAY); } diff --git a/gst/rtp/gstrtpbvdepay.c b/gst/rtp/gstrtpbvdepay.c index f398394357..00c145267f 100644 --- a/gst/rtp/gstrtpbvdepay.c +++ b/gst/rtp/gstrtpbvdepay.c @@ -67,7 +67,7 @@ gst_rtp_bv_depay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_bv_depay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP BroadcomVoice depayloader", "Codec/Depayloader/Network", + "RTP BroadcomVoice depayloader", "Codec/Depayloader/Network/RTP", "Extracts BroadcomVoice audio from RTP packets (RFC 4298)", "Wim Taymans "); } @@ -179,5 +179,5 @@ gboolean gst_rtp_bv_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpbvdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_BV_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_BV_DEPAY); } diff --git a/gst/rtp/gstrtpbvpay.c b/gst/rtp/gstrtpbvpay.c index 1ea343a7e7..7ddc5781c9 100644 --- a/gst/rtp/gstrtpbvpay.c +++ b/gst/rtp/gstrtpbvpay.c @@ -71,7 +71,7 @@ gst_rtp_bv_pay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_bv_pay_src_template)); gst_element_class_set_details_simple (element_class, "RTP BV Payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Packetize BroadcomVoice audio streams into RTP packets (RFC 4298)", "Wim Taymans "); } @@ -219,5 +219,5 @@ gboolean gst_rtp_bv_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpbvpay", - GST_RANK_NONE, GST_TYPE_RTP_BV_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_BV_PAY); } diff --git a/gst/rtp/gstrtpceltdepay.c b/gst/rtp/gstrtpceltdepay.c index 0bf387b1a5..c03a5b4825 100644 --- a/gst/rtp/gstrtpceltdepay.c +++ b/gst/rtp/gstrtpceltdepay.c @@ -83,7 +83,7 @@ gst_rtp_celt_depay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_celt_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP CELT depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts CELT audio from RTP packets", "Wim Taymans "); @@ -271,5 +271,5 @@ gboolean gst_rtp_celt_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpceltdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_CELT_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_CELT_DEPAY); } diff --git a/gst/rtp/gstrtpceltpay.c b/gst/rtp/gstrtpceltpay.c index 7524a27b00..05715c10aa 100644 --- a/gst/rtp/gstrtpceltpay.c +++ b/gst/rtp/gstrtpceltpay.c @@ -75,7 +75,7 @@ gst_rtp_celt_pay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_celt_pay_src_template)); gst_element_class_set_details_simple (element_class, "RTP CELT payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payload-encodes CELT audio into a RTP packet", "Wim Taymans "); @@ -473,5 +473,5 @@ gboolean gst_rtp_celt_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpceltpay", - GST_RANK_NONE, GST_TYPE_RTP_CELT_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_CELT_PAY); } diff --git a/gst/rtp/gstrtpdepay.c b/gst/rtp/gstrtpdepay.c index b037efd298..d94d9b1964 100644 --- a/gst/rtp/gstrtpdepay.c +++ b/gst/rtp/gstrtpdepay.c @@ -72,7 +72,7 @@ gst_rtp_depay_base_init (gpointer klass) gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&gst_rtp_depay_sink_rtcp_template)); gst_element_class_set_details_simple (gstelement_class, - "Dummy RTP session manager", "Codec/Depayloader/Network", + "Dummy RTP session manager", "Codec/Depayloader/Network/RTP", "Accepts raw RTP and RTCP packets and sends them forward", "Wim Taymans "); } @@ -158,5 +158,5 @@ gboolean gst_rtp_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpdepay", - GST_RANK_NONE, GST_TYPE_RTP_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_DEPAY); } diff --git a/gst/rtp/gstrtpdvdepay.c b/gst/rtp/gstrtpdvdepay.c index ca190fa77a..883d1152a2 100644 --- a/gst/rtp/gstrtpdvdepay.c +++ b/gst/rtp/gstrtpdvdepay.c @@ -93,7 +93,7 @@ GST_BOILERPLATE (GstRTPDVDepay, gst_rtp_dv_depay, GstBaseRTPDepayload, gst_static_pad_template_get (&sink_factory)); gst_element_class_set_details_simple (element_class, "RTP DV Depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Depayloads DV from RTP packets (RFC 3189)", "Marcel Moreaux , Wim Taymans "); } @@ -411,5 +411,5 @@ gboolean gst_rtp_dv_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpdvdepay", - GST_RANK_NONE, GST_TYPE_RTP_DV_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_DV_DEPAY); } diff --git a/gst/rtp/gstrtpdvpay.c b/gst/rtp/gstrtpdvpay.c index 7d0c48dc16..1d6230ccf9 100644 --- a/gst/rtp/gstrtpdvpay.c +++ b/gst/rtp/gstrtpdvpay.c @@ -106,7 +106,7 @@ GST_BOILERPLATE (GstRTPDVPay, gst_rtp_dv_pay, GstBaseRTPPayload, gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_dv_pay_src_template)); gst_element_class_set_details_simple (element_class, "RTP DV Payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payloads DV into RTP packets (RFC 3189)", "Marcel Moreaux , Wim Taymans "); } @@ -371,5 +371,5 @@ gboolean gst_rtp_dv_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpdvpay", - GST_RANK_NONE, GST_TYPE_RTP_DV_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_DV_PAY); } diff --git a/gst/rtp/gstrtpg722depay.c b/gst/rtp/gstrtpg722depay.c index ff479c1839..1e892fbc18 100644 --- a/gst/rtp/gstrtpg722depay.c +++ b/gst/rtp/gstrtpg722depay.c @@ -81,7 +81,7 @@ gst_rtp_g722_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_g722_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP audio depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts G722 audio from RTP packets", "Wim Taymans "); } @@ -256,5 +256,5 @@ gboolean gst_rtp_g722_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpg722depay", - GST_RANK_MARGINAL, GST_TYPE_RTP_G722_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_G722_DEPAY); } diff --git a/gst/rtp/gstrtpg722pay.c b/gst/rtp/gstrtpg722pay.c index 56c4a9f406..7346ca149a 100644 --- a/gst/rtp/gstrtpg722pay.c +++ b/gst/rtp/gstrtpg722pay.c @@ -70,7 +70,7 @@ gst_rtp_g722_pay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_g722_pay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP audio payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payload-encode Raw audio into RTP packets (RFC 3551)", "Wim Taymans "); } @@ -206,5 +206,5 @@ gboolean gst_rtp_g722_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpg722pay", - GST_RANK_NONE, GST_TYPE_RTP_G722_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_G722_PAY); } diff --git a/gst/rtp/gstrtpg723depay.c b/gst/rtp/gstrtpg723depay.c index 38215d44f4..b4aa5fe538 100644 --- a/gst/rtp/gstrtpg723depay.c +++ b/gst/rtp/gstrtpg723depay.c @@ -92,7 +92,7 @@ gst_rtp_g723_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_g723_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP G.723 depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts G.723 audio from RTP packets (RFC 3551)", "Wim Taymans "); @@ -224,5 +224,5 @@ gboolean gst_rtp_g723_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpg723depay", - GST_RANK_MARGINAL, GST_TYPE_RTP_G723_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_G723_DEPAY); } diff --git a/gst/rtp/gstrtpg723pay.c b/gst/rtp/gstrtpg723pay.c index 948a8fa22c..439ef0284e 100644 --- a/gst/rtp/gstrtpg723pay.c +++ b/gst/rtp/gstrtpg723pay.c @@ -80,7 +80,7 @@ gst_rtp_g723_pay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_g723_pay_src_template)); gst_element_class_set_details_simple (element_class, "RTP G.723 payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Packetize G.723 audio into RTP packets", "Wim Taymans "); } @@ -311,6 +311,6 @@ gst_rtp_g723_pay_change_state (GstElement * element, GstStateChange transition) gboolean gst_rtp_g723_pay_plugin_init (GstPlugin * plugin) { - return gst_element_register (plugin, "rtpg723pay", GST_RANK_NONE, + return gst_element_register (plugin, "rtpg723pay", GST_RANK_SECONDARY, gst_rtp_g723_pay_get_type ()); } diff --git a/gst/rtp/gstrtpg726depay.c b/gst/rtp/gstrtpg726depay.c index 5f70bc317f..2b36755e71 100644 --- a/gst/rtp/gstrtpg726depay.c +++ b/gst/rtp/gstrtpg726depay.c @@ -100,7 +100,7 @@ gst_rtp_g726_depay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_g726_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP G.726 depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts G.726 audio from RTP packets", "Axis Communications "); } @@ -377,5 +377,5 @@ gboolean gst_rtp_g726_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpg726depay", - GST_RANK_MARGINAL, GST_TYPE_RTP_G726_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_G726_DEPAY); } diff --git a/gst/rtp/gstrtpg726pay.c b/gst/rtp/gstrtpg726pay.c index bd1cb99dca..16086bfd68 100644 --- a/gst/rtp/gstrtpg726pay.c +++ b/gst/rtp/gstrtpg726pay.c @@ -88,7 +88,7 @@ gst_rtp_g726_pay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_g726_pay_src_template)); gst_element_class_set_details_simple (element_class, "RTP G.726 payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payload-encodes G.726 audio into a RTP packet", "Axis Communications "); } @@ -415,5 +415,5 @@ gboolean gst_rtp_g726_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpg726pay", - GST_RANK_NONE, GST_TYPE_RTP_G726_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_G726_PAY); } diff --git a/gst/rtp/gstrtpg729depay.c b/gst/rtp/gstrtpg729depay.c index 6b76a6b63b..3d22508622 100644 --- a/gst/rtp/gstrtpg729depay.c +++ b/gst/rtp/gstrtpg729depay.c @@ -90,7 +90,7 @@ gst_rtp_g729_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_g729_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP G.729 depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts G.729 audio from RTP packets (RFC 3551)", "Laurent Glayal "); @@ -223,5 +223,5 @@ gboolean gst_rtp_g729_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpg729depay", - GST_RANK_MARGINAL, GST_TYPE_RTP_G729_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_G729_DEPAY); } diff --git a/gst/rtp/gstrtpg729pay.c b/gst/rtp/gstrtpg729pay.c index c6a2b403d9..37049d638d 100644 --- a/gst/rtp/gstrtpg729pay.c +++ b/gst/rtp/gstrtpg729pay.c @@ -87,7 +87,7 @@ gst_rtp_g729_pay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_g729_pay_src_template)); gst_element_class_set_details_simple (element_class, "RTP G.729 payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Packetize G.729 audio into RTP packets", "Olivier Crete "); @@ -399,5 +399,5 @@ gboolean gst_rtp_g729_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpg729pay", - GST_RANK_NONE, GST_TYPE_RTP_G729_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_G729_PAY); } diff --git a/gst/rtp/gstrtpgsmdepay.c b/gst/rtp/gstrtpgsmdepay.c index 261bc0dbdb..bb62c50917 100644 --- a/gst/rtp/gstrtpgsmdepay.c +++ b/gst/rtp/gstrtpgsmdepay.c @@ -75,7 +75,7 @@ gst_rtp_gsm_depay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_gsm_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP GSM depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts GSM audio from RTP packets", "Zeeshan Ali "); } @@ -148,5 +148,5 @@ gboolean gst_rtp_gsm_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpgsmdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_GSM_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_GSM_DEPAY); } diff --git a/gst/rtp/gstrtpgsmpay.c b/gst/rtp/gstrtpgsmpay.c index a7dd805f95..ad984bcde1 100644 --- a/gst/rtp/gstrtpgsmpay.c +++ b/gst/rtp/gstrtpgsmpay.c @@ -70,7 +70,7 @@ gst_rtp_gsm_pay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_gsm_pay_src_template)); gst_element_class_set_details_simple (element_class, "RTP GSM payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payload-encodes GSM audio into a RTP packet", "Zeeshan Ali "); } @@ -173,5 +173,5 @@ gboolean gst_rtp_gsm_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpgsmpay", - GST_RANK_NONE, GST_TYPE_RTP_GSM_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_GSM_PAY); } diff --git a/gst/rtp/gstrtpgstdepay.c b/gst/rtp/gstrtpgstdepay.c new file mode 100644 index 0000000000..4e536bafac --- /dev/null +++ b/gst/rtp/gstrtpgstdepay.c @@ -0,0 +1,359 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include +#include "gstrtpgstdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpgstdepay_debug); +#define GST_CAT_DEFAULT (rtpgstdepay_debug) + +static GstStaticPadTemplate gst_rtp_gst_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate gst_rtp_gst_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"application\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"X-GST\"") + ); + +GST_BOILERPLATE (GstRtpGSTDepay, gst_rtp_gst_depay, GstBaseRTPDepayload, + GST_TYPE_BASE_RTP_DEPAYLOAD); + +static void gst_rtp_gst_depay_finalize (GObject * object); + +static GstStateChangeReturn gst_rtp_gst_depay_change_state (GstElement * + element, GstStateChange transition); + +static void gst_rtp_gst_depay_reset (GstRtpGSTDepay * rtpgstdepay); +static gboolean gst_rtp_gst_depay_setcaps (GstBaseRTPDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_gst_depay_process (GstBaseRTPDepayload * depayload, + GstBuffer * buf); + +static void +gst_rtp_gst_depay_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_gst_depay_src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_gst_depay_sink_template)); + + gst_element_class_set_details_simple (element_class, + "GStreamer depayloader", "Codec/Depayloader/Network", + "Extracts GStreamer buffers from RTP packets", + "Wim Taymans "); +} + +static void +gst_rtp_gst_depay_class_init (GstRtpGSTDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseRTPDepayloadClass *gstbasertpdepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasertpdepayload_class = (GstBaseRTPDepayloadClass *) klass; + + gobject_class->finalize = gst_rtp_gst_depay_finalize; + + gstelement_class->change_state = gst_rtp_gst_depay_change_state; + + gstbasertpdepayload_class->set_caps = gst_rtp_gst_depay_setcaps; + gstbasertpdepayload_class->process = gst_rtp_gst_depay_process; + + GST_DEBUG_CATEGORY_INIT (rtpgstdepay_debug, "rtpgstdepay", 0, + "Gstreamer RTP Depayloader"); +} + +static void +gst_rtp_gst_depay_init (GstRtpGSTDepay * rtpgstdepay, + GstRtpGSTDepayClass * klass) +{ + rtpgstdepay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_gst_depay_finalize (GObject * object) +{ + GstRtpGSTDepay *rtpgstdepay; + + rtpgstdepay = GST_RTP_GST_DEPAY (object); + + gst_rtp_gst_depay_reset (rtpgstdepay); + g_object_unref (rtpgstdepay->adapter); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +store_cache (GstRtpGSTDepay * rtpgstdepay, guint CV, GstCaps * caps) +{ + if (rtpgstdepay->CV_cache[CV]) + gst_caps_unref (rtpgstdepay->CV_cache[CV]); + rtpgstdepay->CV_cache[CV] = caps; +} + +static void +gst_rtp_gst_depay_reset (GstRtpGSTDepay * rtpgstdepay) +{ + guint i; + + gst_adapter_clear (rtpgstdepay->adapter); + rtpgstdepay->current_CV = 0; + for (i = 0; i < 8; i++) + store_cache (rtpgstdepay, i, NULL); +} + +static gboolean +gst_rtp_gst_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) +{ + GstRtpGSTDepay *rtpgstdepay; + GstStructure *structure; + GstCaps *outcaps; + gint clock_rate; + gboolean res; + const gchar *capsenc; + gchar *capsstr; + + rtpgstdepay = GST_RTP_GST_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; + depayload->clock_rate = clock_rate; + + capsenc = gst_structure_get_string (structure, "caps"); + if (capsenc) { + gsize out_len; + + capsstr = (gchar *) g_base64_decode (capsenc, &out_len); + outcaps = gst_caps_from_string (capsstr); + g_free (capsstr); + + /* we have the SDP caps as output caps */ + rtpgstdepay->current_CV = 0; + gst_caps_ref (outcaps); + store_cache (rtpgstdepay, 0, outcaps); + } else { + outcaps = gst_caps_new_any (); + } + res = gst_pad_set_caps (depayload->srcpad, outcaps); + gst_caps_unref (outcaps); + + return res; +} + +static GstBuffer * +gst_rtp_gst_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) +{ + GstRtpGSTDepay *rtpgstdepay; + GstBuffer *subbuf, *outbuf = NULL; + gint payload_len; + guint8 *payload; + guint16 frag_offset; + guint CV; + + rtpgstdepay = GST_RTP_GST_DEPAY (depayload); + + payload_len = gst_rtp_buffer_get_payload_len (buf); + + if (payload_len <= 8) + goto empty_packet; + + if (GST_BUFFER_IS_DISCONT (buf)) { + GST_WARNING_OBJECT (rtpgstdepay, "DISCONT, clear adapter"); + gst_adapter_clear (rtpgstdepay->adapter); + } + + payload = gst_rtp_buffer_get_payload (buf); + + /* strip off header + * + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |C| CV |D|X|Y|Z| MBZ | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Frag_offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + frag_offset = + (payload[4] << 24) | (payload[5] << 16) | (payload[6] << 8) | payload[7]; + + /* subbuffer skipping the 8 header bytes */ + subbuf = gst_rtp_buffer_get_payload_subbuffer (buf, 8, -1); + gst_adapter_push (rtpgstdepay->adapter, subbuf); + + if (gst_rtp_buffer_get_marker (buf)) { + guint avail; + GstCaps *outcaps; + + /* take the buffer */ + avail = gst_adapter_available (rtpgstdepay->adapter); + outbuf = gst_adapter_take_buffer (rtpgstdepay->adapter, avail); + + CV = (payload[0] >> 4) & 0x7; + + if (payload[0] & 0x80) { + guint b, csize, size, offset; + guint8 *data; + GstBuffer *subbuf; + + /* C bit, we have inline caps */ + data = GST_BUFFER_DATA (outbuf); + size = GST_BUFFER_SIZE (outbuf); + + /* start reading the length, we need this to skip to the data later */ + csize = offset = 0; + do { + if (offset >= size) + goto too_small; + b = data[offset++]; + csize = (csize << 7) | (b & 0x7f); + } while (b & 0x80); + + if (size < csize) + goto too_small; + + /* parse and store in cache */ + outcaps = gst_caps_from_string ((gchar *) & data[offset]); + store_cache (rtpgstdepay, CV, outcaps); + + /* skip caps */ + offset += csize; + size -= csize; + + GST_DEBUG_OBJECT (rtpgstdepay, + "inline caps %u, length %u, %" GST_PTR_FORMAT, CV, csize, outcaps); + + /* create real data buffer when needed */ + if (size) + subbuf = gst_buffer_create_sub (outbuf, offset, size); + else + subbuf = NULL; + + gst_buffer_unref (outbuf); + outbuf = subbuf; + } + + /* see what caps we need */ + if (CV != rtpgstdepay->current_CV) { + /* we need to switch caps, check if we have the caps */ + if ((outcaps = rtpgstdepay->CV_cache[CV]) == NULL) + goto missing_caps; + + GST_DEBUG_OBJECT (rtpgstdepay, + "need caps switch from %u to %u, %" GST_PTR_FORMAT, + rtpgstdepay->current_CV, CV, outcaps); + + /* and set caps */ + if (gst_pad_set_caps (depayload->srcpad, outcaps)) + rtpgstdepay->current_CV = CV; + } + + if (outbuf) { + if (payload[0] & 0x8) + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); + if (payload[0] & 0x4) + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_MEDIA1); + if (payload[0] & 0x2) + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_MEDIA2); + if (payload[0] & 0x1) + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_MEDIA3); + } + } + return outbuf; + + /* ERRORS */ +empty_packet: + { + GST_ELEMENT_WARNING (rtpgstdepay, STREAM, DECODE, + ("Empty Payload."), (NULL)); + return NULL; + } +too_small: + { + GST_ELEMENT_WARNING (rtpgstdepay, STREAM, DECODE, + ("Buffer too small."), (NULL)); + if (outbuf) + gst_buffer_unref (outbuf); + return NULL; + } +missing_caps: + { + GST_ELEMENT_WARNING (rtpgstdepay, STREAM, DECODE, + ("Missing caps %u.", CV), (NULL)); + if (outbuf) + gst_buffer_unref (outbuf); + return NULL; + } +} + +static GstStateChangeReturn +gst_rtp_gst_depay_change_state (GstElement * element, GstStateChange transition) +{ + GstRtpGSTDepay *rtpgstdepay; + GstStateChangeReturn ret; + + rtpgstdepay = GST_RTP_GST_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_gst_depay_reset (rtpgstdepay); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_gst_depay_reset (rtpgstdepay); + break; + default: + break; + } + return ret; +} + + +gboolean +gst_rtp_gst_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpgstdepay", + GST_RANK_MARGINAL, GST_TYPE_RTP_GST_DEPAY); +} diff --git a/gst/rtp/gstrtpgstdepay.h b/gst/rtp/gstrtpgstdepay.h new file mode 100644 index 0000000000..7a50856d32 --- /dev/null +++ b/gst/rtp/gstrtpgstdepay.h @@ -0,0 +1,63 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_RTP_GST_DEPAY_H__ +#define __GST_RTP_GST_DEPAY_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_GST_DEPAY \ + (gst_rtp_gst_depay_get_type()) +#define GST_RTP_GST_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_GST_DEPAY,GstRtpGSTDepay)) +#define GST_RTP_GST_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_GST_DEPAY,GstRtpGSTDepayClass)) +#define GST_IS_RTP_GST_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_GST_DEPAY)) +#define GST_IS_RTP_GST_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_GST_DEPAY)) + +typedef struct _GstRtpGSTDepay GstRtpGSTDepay; +typedef struct _GstRtpGSTDepayClass GstRtpGSTDepayClass; + +struct _GstRtpGSTDepay +{ + GstBaseRTPDepayload depayload; + + GstAdapter *adapter; + guint current_CV; + GstCaps *CV_cache[8]; +}; + +struct _GstRtpGSTDepayClass +{ + GstBaseRTPDepayloadClass parent_class; +}; + +GType gst_rtp_gst_depay_get_type (void); + +gboolean gst_rtp_gst_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_GST_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpgstpay.c b/gst/rtp/gstrtpgstpay.c new file mode 100644 index 0000000000..3e1aaad9dc --- /dev/null +++ b/gst/rtp/gstrtpgstpay.c @@ -0,0 +1,239 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include + +#include "gstrtpgstpay.h" + +/* + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |C| CV |D|X|Y|Z| MBZ | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Frag_offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * C: caps inlined flag + * When C set, first part of payload contains caps definition. Caps definition + * starts with variable-length length prefix and then a string of that length. + * the length is encoded in big endian 7 bit chunks, the top 1 bit of a byte + * is the continuation marker and the 7 next bits the data. A continuation + * marker of 1 means that the next byte contains more data. + * + * CV: caps version, 0 = caps from SDP, 1 - 7 inlined caps + * D: delta unit buffer + * X: media 1 flag + * Y: media 2 flag + * Z: media 3 flag + * + * + */ + +static GstStaticPadTemplate gst_rtp_gst_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate gst_rtp_gst_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"application\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"X-GST\"") + ); + +static void gst_rtp_gst_pay_finalize (GObject * object); + +static gboolean gst_rtp_gst_pay_setcaps (GstBaseRTPPayload * payload, + GstCaps * caps); +static GstFlowReturn gst_rtp_gst_pay_handle_buffer (GstBaseRTPPayload * payload, + GstBuffer * buffer); + +GST_BOILERPLATE (GstRtpGSTPay, gst_rtp_gst_pay, GstBaseRTPPayload, + GST_TYPE_BASE_RTP_PAYLOAD) + + static void gst_rtp_gst_pay_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_gst_pay_src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_gst_pay_sink_template)); + + gst_element_class_set_details_simple (element_class, + "RTP GStreamer payloader", "Codec/Payloader/Network/RTP", + "Payload GStreamer buffers as RTP packets", + "Wim Taymans "); +} + +static void +gst_rtp_gst_pay_class_init (GstRtpGSTPayClass * klass) +{ + GObjectClass *gobject_class; + GstBaseRTPPayloadClass *gstbasertppayload_class; + + gobject_class = (GObjectClass *) klass; + gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass; + + gobject_class->finalize = gst_rtp_gst_pay_finalize; + + gstbasertppayload_class->set_caps = gst_rtp_gst_pay_setcaps; + gstbasertppayload_class->handle_buffer = gst_rtp_gst_pay_handle_buffer; +} + +static void +gst_rtp_gst_pay_init (GstRtpGSTPay * rtpgstpay, GstRtpGSTPayClass * klass) +{ +} + +static void +gst_rtp_gst_pay_finalize (GObject * object) +{ + GstRtpGSTPay *rtpgstpay; + + rtpgstpay = GST_RTP_GST_PAY (object); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_rtp_gst_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) +{ + gboolean res; + gchar *capsstr, *capsenc; + + capsstr = gst_caps_to_string (caps); + capsenc = g_base64_encode ((guchar *) capsstr, strlen (capsstr)); + g_free (capsstr); + + gst_basertppayload_set_options (payload, "application", TRUE, "X-GST", 90000); + res = + gst_basertppayload_set_outcaps (payload, "caps", G_TYPE_STRING, capsenc, + NULL); + g_free (capsenc); + + return res; +} + +static GstFlowReturn +gst_rtp_gst_pay_handle_buffer (GstBaseRTPPayload * basepayload, + GstBuffer * buffer) +{ + GstRtpGSTPay *rtpgstpay; + guint8 *data; + guint size; + GstBuffer *outbuf; + GstFlowReturn ret; + GstClockTime timestamp; + guint16 frag_offset; + guint flags; + + rtpgstpay = GST_RTP_GST_PAY (basepayload); + + size = GST_BUFFER_SIZE (buffer); + data = GST_BUFFER_DATA (buffer); + timestamp = GST_BUFFER_TIMESTAMP (buffer); + + ret = GST_FLOW_OK; + + /* caps always from SDP for now */ + flags = 0; + if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) + flags |= (1 << 3); + if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_MEDIA1)) + flags |= (1 << 2); + if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_MEDIA2)) + flags |= (1 << 1); + if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_MEDIA3)) + flags |= (1 << 0); + + /* + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |C| CV |D|X|Y|Z| MBZ | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Frag_offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + frag_offset = 0; + + while (size > 0) { + guint towrite; + guint8 *payload; + guint payload_len; + guint packet_len; + + /* this will be the total lenght of the packet */ + packet_len = gst_rtp_buffer_calc_packet_len (8 + size, 0, 0); + + /* fill one MTU or all available bytes */ + towrite = MIN (packet_len, GST_BASE_RTP_PAYLOAD_MTU (rtpgstpay)); + + /* this is the payload length */ + payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); + + /* create buffer to hold the payload */ + outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); + payload = gst_rtp_buffer_get_payload (outbuf); + + payload[0] = flags; + payload[1] = payload[2] = payload[3] = 0; + payload[4] = frag_offset >> 24; + payload[5] = frag_offset >> 16; + payload[6] = frag_offset >> 8; + payload[7] = frag_offset & 0xff; + + payload += 8; + payload_len -= 8; + + memcpy (payload, data, payload_len); + + data += payload_len; + size -= payload_len; + frag_offset += payload_len; + + if (size == 0) + gst_rtp_buffer_set_marker (outbuf, TRUE); + + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + + ret = gst_basertppayload_push (basepayload, outbuf); + } + + return ret; +} + +gboolean +gst_rtp_gst_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpgstpay", + GST_RANK_NONE, GST_TYPE_RTP_GST_PAY); +} diff --git a/gst/rtp/gstrtpgstpay.h b/gst/rtp/gstrtpgstpay.h new file mode 100644 index 0000000000..c7a7c02744 --- /dev/null +++ b/gst/rtp/gstrtpgstpay.h @@ -0,0 +1,58 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_RTP_GST_PAY_H__ +#define __GST_RTP_GST_PAY_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_GST_PAY \ + (gst_rtp_gst_pay_get_type()) +#define GST_RTP_GST_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_GST_PAY,GstRtpGSTPay)) +#define GST_RTP_GST_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_GST_PAY,GstRtpGSTPayClass)) +#define GST_IS_RTP_GST_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_GST_PAY)) +#define GST_IS_RTP_GST_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_GST_PAY)) + +typedef struct _GstRtpGSTPay GstRtpGSTPay; +typedef struct _GstRtpGSTPayClass GstRtpGSTPayClass; + +struct _GstRtpGSTPay +{ + GstBaseRTPPayload payload; +}; + +struct _GstRtpGSTPayClass +{ + GstBaseRTPPayloadClass parent_class; +}; + +GType gst_rtp_gst_pay_get_type (void); + +gboolean gst_rtp_gst_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_GST_PAY_H__ */ diff --git a/gst/rtp/gstrtph263depay.c b/gst/rtp/gstrtph263depay.c index 8ae5a28862..4bc8704265 100644 --- a/gst/rtp/gstrtph263depay.c +++ b/gst/rtp/gstrtph263depay.c @@ -85,7 +85,7 @@ gst_rtp_h263_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_h263_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP H263 depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts H263 video from RTP packets (RFC 2190)", "Philippe Kalaf , " "Edward Hervey "); @@ -377,5 +377,5 @@ gboolean gst_rtp_h263_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtph263depay", - GST_RANK_MARGINAL, GST_TYPE_RTP_H263_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_H263_DEPAY); } diff --git a/gst/rtp/gstrtph263pay.c b/gst/rtp/gstrtph263pay.c index 5604ee0ab4..8eb85bc8cd 100644 --- a/gst/rtp/gstrtph263pay.c +++ b/gst/rtp/gstrtph263pay.c @@ -408,7 +408,7 @@ GST_BOILERPLATE (GstRtpH263Pay, gst_rtp_h263_pay, GstBaseRTPPayload, gst_static_pad_template_get (&gst_rtp_h263_pay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP H263 packet payloader", "Codec/Payloader/Network", + "RTP H263 packet payloader", "Codec/Payloader/Network/RTP", "Payload-encodes H263 video in RTP packets (RFC 2190)", "Neil Stratford " "Dejan Sakelsak "); @@ -1796,5 +1796,5 @@ gboolean gst_rtp_h263_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtph263pay", - GST_RANK_NONE, GST_TYPE_RTP_H263_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_H263_PAY); } diff --git a/gst/rtp/gstrtph263pdepay.c b/gst/rtp/gstrtph263pdepay.c index 9be2f366e4..5cad4bf91b 100644 --- a/gst/rtp/gstrtph263pdepay.c +++ b/gst/rtp/gstrtph263pdepay.c @@ -103,7 +103,7 @@ gst_rtp_h263p_depay_base_init (gpointer klass) gst_element_class_set_details_simple (element_class, "RTP H263 depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts H263/+/++ video from RTP packets (RFC 4629)", "Wim Taymans "); } @@ -395,5 +395,5 @@ gboolean gst_rtp_h263p_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtph263pdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_H263P_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_H263P_DEPAY); } diff --git a/gst/rtp/gstrtph263ppay.c b/gst/rtp/gstrtph263ppay.c index 0a547db7c8..fff68ecc4a 100644 --- a/gst/rtp/gstrtph263ppay.c +++ b/gst/rtp/gstrtph263ppay.c @@ -104,7 +104,7 @@ gst_rtp_h263p_pay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_h263p_pay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP H263 payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payload-encodes H263/+/++ video in RTP packets (RFC 4629)", "Wim Taymans "); } @@ -333,5 +333,5 @@ gboolean gst_rtp_h263p_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtph263ppay", - GST_RANK_NONE, GST_TYPE_RTP_H263P_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_H263P_PAY); } diff --git a/gst/rtp/gstrtph264depay.c b/gst/rtp/gstrtph264depay.c index 915c9fdf11..0d62628384 100644 --- a/gst/rtp/gstrtph264depay.c +++ b/gst/rtp/gstrtph264depay.c @@ -107,7 +107,7 @@ gst_rtp_h264_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_h264_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP H264 depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts H264 video from RTP packets (RFC 3984)", "Wim Taymans "); } @@ -130,12 +130,12 @@ gst_rtp_h264_depay_class_init (GstRtpH264DepayClass * klass) g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BYTE_STREAM, g_param_spec_boolean ("byte-stream", "Byte Stream", - "Generate byte stream format of NALU", DEFAULT_BYTE_STREAM, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + "Generate byte stream format of NALU (deprecated; use caps)", + DEFAULT_BYTE_STREAM, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ACCESS_UNIT, g_param_spec_boolean ("access-unit", "Access Unit", - "Merge NALU into AU (picture)", DEFAULT_ACCESS_UNIT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + "Merge NALU into AU (picture) (deprecated; use caps)", + DEFAULT_ACCESS_UNIT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gstelement_class->change_state = gst_rtp_h264_depay_change_state; @@ -214,6 +214,65 @@ gst_rtp_h264_depay_get_property (GObject * object, guint prop_id, } } +static void +gst_rtp_h264_depay_negotiate (GstRtpH264Depay * rtph264depay) +{ + GstCaps *caps; + gint byte_stream = -1; + gint merge = -1; + + caps = + gst_pad_get_allowed_caps (GST_BASE_RTP_DEPAYLOAD_SRCPAD (rtph264depay)); + + GST_DEBUG_OBJECT (rtph264depay, "allowed caps: %" GST_PTR_FORMAT, caps); + + if (caps) { + if (gst_caps_get_size (caps) > 0) { + GstStructure *s = gst_caps_get_structure (caps, 0); + const gchar *str = NULL; + + if ((str = gst_structure_get_string (s, "stream-format"))) { + if (strcmp (str, "avc") == 0) { + byte_stream = FALSE; + } else if (strcmp (str, "byte-stream") == 0) { + byte_stream = TRUE; + } else { + GST_DEBUG_OBJECT (rtph264depay, "unknown stream-format: %s", str); + } + } + + if ((str = gst_structure_get_string (s, "alignment"))) { + if (strcmp (str, "au") == 0) { + merge = TRUE; + } else if (strcmp (str, "nal") == 0) { + merge = FALSE; + } else { + GST_DEBUG_OBJECT (rtph264depay, "unknown alignment: %s", str); + } + } + } + gst_caps_unref (caps); + } + + if (byte_stream >= 0) { + GST_DEBUG_OBJECT (rtph264depay, "downstream requires byte-stream %d", + byte_stream); + if (rtph264depay->byte_stream != byte_stream) { + GST_WARNING_OBJECT (rtph264depay, + "overriding property setting based on caps"); + rtph264depay->byte_stream = byte_stream; + } + } + if (merge >= 0) { + GST_DEBUG_OBJECT (rtph264depay, "downstream requires merge %d", merge); + if (rtph264depay->merge != merge) { + GST_WARNING_OBJECT (rtph264depay, + "overriding property setting based on caps"); + rtph264depay->merge = merge; + } + } +} + static gboolean gst_rtp_h264_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) { @@ -239,6 +298,9 @@ gst_rtp_h264_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) /* hex: AVCProfileIndication:8 | profile_compat:8 | AVCLevelIndication:8 */ profile = gst_structure_get_string (structure, "profile-level-id"); + /* negotiate with downstream w.r.t. output format and alignment */ + gst_rtp_h264_depay_negotiate (rtph264depay); + if (rtph264depay->byte_stream && ps != NULL) { /* for bytestream we only need the parameter sets but we don't error out * when they are not there, we assume they are in the stream. */ @@ -383,8 +445,13 @@ gst_rtp_h264_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) gst_caps_set_simple (srccaps, "codec_data", GST_TYPE_BUFFER, codec_data, NULL); + gst_buffer_unref (codec_data); } + gst_caps_set_simple (srccaps, "stream-format", G_TYPE_STRING, + rtph264depay->byte_stream ? "byte-stream" : "avc", + "alignment", G_TYPE_STRING, rtph264depay->merge ? "au" : "nal", NULL); + res = gst_pad_set_caps (depayload->srcpad, srccaps); gst_caps_unref (srccaps); @@ -806,5 +873,5 @@ gboolean gst_rtp_h264_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtph264depay", - GST_RANK_MARGINAL, GST_TYPE_RTP_H264_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_H264_DEPAY); } diff --git a/gst/rtp/gstrtph264pay.c b/gst/rtp/gstrtph264pay.c index d90481e86f..15b04705d5 100644 --- a/gst/rtp/gstrtph264pay.c +++ b/gst/rtp/gstrtph264pay.c @@ -130,7 +130,7 @@ gst_rtp_h264_pay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_h264_pay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP H264 payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payload-encode H264 video into RTP packets (RFC 3984)", "Laurent Glayal "); } @@ -1114,11 +1114,12 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload, return ret; caps_rejected: - - GST_WARNING_OBJECT (basepayload, "Could not set outcaps"); - g_array_set_size (nal_queue, 0); - gst_buffer_unref (buffer); - return GST_FLOW_NOT_NEGOTIATED; + { + GST_WARNING_OBJECT (basepayload, "Could not set outcaps"); + g_array_set_size (nal_queue, 0); + gst_buffer_unref (buffer); + return GST_FLOW_NOT_NEGOTIATED; + } } static gboolean @@ -1128,6 +1129,9 @@ gst_rtp_h264_pay_handle_event (GstPad * pad, GstEvent * event) GstRtpH264Pay *rtph264pay = GST_RTP_H264_PAY (GST_PAD_PARENT (pad)); switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_adapter_clear (rtph264pay->adapter); + break; case GST_EVENT_CUSTOM_DOWNSTREAM: s = gst_event_get_structure (event); if (gst_structure_has_name (s, "GstForceKeyUnit")) { @@ -1230,5 +1234,5 @@ gboolean gst_rtp_h264_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtph264pay", - GST_RANK_NONE, GST_TYPE_RTP_H264_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_H264_PAY); } diff --git a/gst/rtp/gstrtpilbcdepay.c b/gst/rtp/gstrtpilbcdepay.c index 381abca5fe..18ca426a7c 100644 --- a/gst/rtp/gstrtpilbcdepay.c +++ b/gst/rtp/gstrtpilbcdepay.c @@ -101,7 +101,7 @@ gst_rtp_ilbc_depay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_ilbc_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP iLBC depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts iLBC audio from RTP packets (RFC 3952)", "Philippe Kalaf "); } @@ -232,5 +232,5 @@ gboolean gst_rtp_ilbc_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpilbcdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_ILBC_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_ILBC_DEPAY); } diff --git a/gst/rtp/gstrtpilbcpay.c b/gst/rtp/gstrtpilbcpay.c index c9381a48ac..c070d6ca10 100644 --- a/gst/rtp/gstrtpilbcpay.c +++ b/gst/rtp/gstrtpilbcpay.c @@ -66,7 +66,7 @@ gst_rtp_ilbc_pay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_ilbc_pay_src_template)); gst_element_class_set_details_simple (element_class, "RTP iLBC Payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Packetize iLBC audio streams into RTP packets", "Philippe Kalaf "); } @@ -212,5 +212,5 @@ gboolean gst_rtp_ilbc_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpilbcpay", - GST_RANK_NONE, GST_TYPE_RTP_ILBC_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_ILBC_PAY); } diff --git a/gst/rtp/gstrtpj2kdepay.c b/gst/rtp/gstrtpj2kdepay.c index 83ccd9c4a7..4ac7c34d5f 100644 --- a/gst/rtp/gstrtpj2kdepay.c +++ b/gst/rtp/gstrtpj2kdepay.c @@ -46,11 +46,35 @@ GST_STATIC_PAD_TEMPLATE ("sink", "clock-rate = (int) 90000, " "encoding-name = (string) \"JPEG2000\"") ); +typedef enum +{ + J2K_MARKER = 0xFF, + J2K_MARKER_SOC = 0x4F, + J2K_MARKER_SOT = 0x90, + J2K_MARKER_SOP = 0x91, + J2K_MARKER_SOD = 0x93, + J2K_MARKER_EOC = 0xD9 +} RtpJ2KMarker; + +#define DEFAULT_BUFFER_LIST TRUE + +enum +{ + PROP_0, + PROP_BUFFER_LIST, + PROP_LAST +}; + GST_BOILERPLATE (GstRtpJ2KDepay, gst_rtp_j2k_depay, GstBaseRTPDepayload, GST_TYPE_BASE_RTP_DEPAYLOAD); static void gst_rtp_j2k_depay_finalize (GObject * object); +static void gst_rtp_j2k_depay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rtp_j2k_depay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + static GstStateChangeReturn gst_rtp_j2k_depay_change_state (GstElement * element, GstStateChange transition); @@ -71,7 +95,7 @@ gst_rtp_j2k_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_j2k_depay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP JPEG 2000 depayloader", "Codec/Depayloader/Network", + "RTP JPEG 2000 depayloader", "Codec/Depayloader/Network/RTP", "Extracts JPEG 2000 video from RTP packets (RFC 5371)", "Wim Taymans "); } @@ -89,6 +113,14 @@ gst_rtp_j2k_depay_class_init (GstRtpJ2KDepayClass * klass) gobject_class->finalize = gst_rtp_j2k_depay_finalize; + gobject_class->set_property = gst_rtp_j2k_depay_set_property; + gobject_class->get_property = gst_rtp_j2k_depay_get_property; + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_LIST, + g_param_spec_boolean ("buffer-list", "Buffer List", + "Use Buffer Lists", + DEFAULT_BUFFER_LIST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gstelement_class->change_state = gst_rtp_j2k_depay_change_state; gstbasertpdepayload_class->set_caps = gst_rtp_j2k_depay_setcaps; @@ -102,7 +134,42 @@ static void gst_rtp_j2k_depay_init (GstRtpJ2KDepay * rtpj2kdepay, GstRtpJ2KDepayClass * klass) { - rtpj2kdepay->adapter = gst_adapter_new (); + rtpj2kdepay->buffer_list = DEFAULT_BUFFER_LIST; + + rtpj2kdepay->pu_adapter = gst_adapter_new (); + rtpj2kdepay->t_adapter = gst_adapter_new (); + rtpj2kdepay->f_adapter = gst_adapter_new (); +} + +static void +store_mheader (GstRtpJ2KDepay * rtpj2kdepay, guint idx, GstBuffer * buf) +{ + GstBuffer *old; + + GST_DEBUG_OBJECT (rtpj2kdepay, "storing main header %p at index %u", buf, + idx); + if ((old = rtpj2kdepay->MH[idx])) + gst_buffer_unref (old); + rtpj2kdepay->MH[idx] = buf; +} + +static void +clear_mheaders (GstRtpJ2KDepay * rtpj2kdepay) +{ + guint i; + + for (i = 0; i < 8; i++) + store_mheader (rtpj2kdepay, i, NULL); +} + +static void +gst_rtp_j2k_depay_reset (GstRtpJ2KDepay * rtpj2kdepay) +{ + clear_mheaders (rtpj2kdepay); + gst_adapter_clear (rtpj2kdepay->pu_adapter); + gst_adapter_clear (rtpj2kdepay->t_adapter); + gst_adapter_clear (rtpj2kdepay->f_adapter); + rtpj2kdepay->next_frag = 0; } static void @@ -112,8 +179,11 @@ gst_rtp_j2k_depay_finalize (GObject * object) rtpj2kdepay = GST_RTP_J2K_DEPAY (object); - g_object_unref (rtpj2kdepay->adapter); - rtpj2kdepay->adapter = NULL; + clear_mheaders (rtpj2kdepay); + + g_object_unref (rtpj2kdepay->pu_adapter); + g_object_unref (rtpj2kdepay->t_adapter); + g_object_unref (rtpj2kdepay->f_adapter); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -142,65 +212,187 @@ gst_rtp_j2k_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) return res; } -static GstBuffer * -gst_rtp_j2k_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) +static void +gst_rtp_j2k_depay_clear_pu (GstRtpJ2KDepay * rtpj2kdepay) +{ + gst_adapter_clear (rtpj2kdepay->pu_adapter); + rtpj2kdepay->have_sync = FALSE; +} + +static GstFlowReturn +gst_rtp_j2k_depay_flush_pu (GstBaseRTPDepayload * depayload) { GstRtpJ2KDepay *rtpj2kdepay; - GstBuffer *outbuf; - guint8 *payload; - guint frag_offset; + GstBuffer *mheader; + guint avail, MHF, mh_id; rtpj2kdepay = GST_RTP_J2K_DEPAY (depayload); - /* flush everything on discont for now */ - if (GST_BUFFER_IS_DISCONT (buf)) { - GST_DEBUG_OBJECT (rtpj2kdepay, "DISCONT, flushing data"); - gst_adapter_clear (rtpj2kdepay->adapter); - rtpj2kdepay->need_header = TRUE; + /* take all available buffers */ + avail = gst_adapter_available (rtpj2kdepay->pu_adapter); + if (avail == 0) + goto done; + + MHF = rtpj2kdepay->pu_MHF; + mh_id = rtpj2kdepay->last_mh_id; + + GST_DEBUG_OBJECT (rtpj2kdepay, "flushing PU of size %u", avail); + + if (MHF == 0) { + GList *packets, *walk; + + packets = gst_adapter_take_list (rtpj2kdepay->pu_adapter, avail); + /* append packets */ + for (walk = packets; walk; walk = g_list_next (walk)) { + GstBuffer *buf = GST_BUFFER_CAST (walk->data); + GST_DEBUG_OBJECT (rtpj2kdepay, "append pu packet of size %u", + GST_BUFFER_SIZE (buf)); + gst_adapter_push (rtpj2kdepay->t_adapter, buf); + } + g_list_free (packets); + } else { + /* we have a header */ + GST_DEBUG_OBJECT (rtpj2kdepay, "keeping header %u", mh_id); + /* we managed to see the start and end of the header, take all from + * adapter and keep in header */ + mheader = gst_adapter_take_buffer (rtpj2kdepay->pu_adapter, avail); + + store_mheader (rtpj2kdepay, mh_id, mheader); } - if (gst_rtp_buffer_get_payload_len (buf) < 8) - goto empty_packet; +done: + rtpj2kdepay->have_sync = FALSE; - payload = gst_rtp_buffer_get_payload (buf); + return GST_FLOW_OK; +} - /* - * 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 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * |tp |MHF|mh_id|T| priority | tile number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * |reserved | fragment offset | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - frag_offset = (payload[5] << 16) | (payload[6] << 8) | payload[7]; +static GstFlowReturn +gst_rtp_j2k_depay_flush_tile (GstBaseRTPDepayload * depayload) +{ + GstRtpJ2KDepay *rtpj2kdepay; + guint avail, mh_id; + GList *packets, *walk; + guint8 end[2]; + GstFlowReturn ret = GST_FLOW_OK; - GST_DEBUG_OBJECT (rtpj2kdepay, "frag %u", frag_offset); + rtpj2kdepay = GST_RTP_J2K_DEPAY (depayload); - if (rtpj2kdepay->need_header) { - if (frag_offset != 0) + /* flush pending PU */ + gst_rtp_j2k_depay_flush_pu (depayload); + + /* take all available buffers */ + avail = gst_adapter_available (rtpj2kdepay->t_adapter); + if (avail == 0) + goto done; + + mh_id = rtpj2kdepay->last_mh_id; + + GST_DEBUG_OBJECT (rtpj2kdepay, "flushing tile of size %u", avail); + + if (gst_adapter_available (rtpj2kdepay->f_adapter) == 0) { + GstBuffer *mheader; + + /* we need a header now */ + if ((mheader = rtpj2kdepay->MH[mh_id]) == NULL) goto waiting_header; - rtpj2kdepay->need_header = FALSE; + /* push header in the adapter */ + GST_DEBUG_OBJECT (rtpj2kdepay, "pushing header %u", mh_id); + gst_adapter_push (rtpj2kdepay->f_adapter, gst_buffer_ref (mheader)); } - /* take JPEG 2000 data, push in the adapter */ - outbuf = gst_rtp_buffer_get_payload_subbuffer (buf, 8, -1); - gst_adapter_push (rtpj2kdepay->adapter, outbuf); - outbuf = NULL; + /* check for last bytes */ + gst_adapter_copy (rtpj2kdepay->t_adapter, end, avail - 2, 2); - if (gst_rtp_buffer_get_marker (buf)) { - guint avail; - guint8 end[2]; - guint8 *data; + /* now append the tile packets to the frame */ + packets = gst_adapter_take_list (rtpj2kdepay->t_adapter, avail); + for (walk = packets; walk; walk = g_list_next (walk)) { + GstBuffer *buf = GST_BUFFER_CAST (walk->data); - /* last buffer take all data out of the adapter */ - avail = gst_adapter_available (rtpj2kdepay->adapter); - GST_DEBUG_OBJECT (rtpj2kdepay, "marker set, last buffer"); + if (walk == packets) { + guint8 *data; + guint size; + + /* first buffer should contain the SOT */ + data = GST_BUFFER_DATA (buf); + size = GST_BUFFER_SIZE (buf); + + if (size < 12) + goto invalid_tile; + + if (data[0] == 0xff && data[1] == J2K_MARKER_SOT) { + guint Psot, nPsot; + + if (end[0] == 0xff && end[1] == J2K_MARKER_EOC) + nPsot = avail - 2; + else + nPsot = avail; + + Psot = GST_READ_UINT32_BE (&data[6]); + if (Psot != nPsot && Psot != 0) { + /* Psot must match the size of the tile */ + GST_DEBUG_OBJECT (rtpj2kdepay, "set Psot from %u to %u", Psot, nPsot); + buf = gst_buffer_make_writable (buf); + data = GST_BUFFER_DATA (buf); + GST_WRITE_UINT32_BE (&data[6], nPsot); + } + } + } + + GST_DEBUG_OBJECT (rtpj2kdepay, "append pu packet of size %u", + GST_BUFFER_SIZE (buf)); + gst_adapter_push (rtpj2kdepay->f_adapter, buf); + } + g_list_free (packets); + +done: + rtpj2kdepay->last_tile = -1; + + return ret; + + /* errors */ +waiting_header: + { + GST_DEBUG_OBJECT (rtpj2kdepay, "waiting for header %u", mh_id); + gst_adapter_clear (rtpj2kdepay->t_adapter); + rtpj2kdepay->last_tile = -1; + return ret; + } +invalid_tile: + { + GST_ELEMENT_WARNING (rtpj2kdepay, STREAM, DECODE, ("Invalid tile"), (NULL)); + gst_adapter_clear (rtpj2kdepay->t_adapter); + rtpj2kdepay->last_tile = -1; + return ret; + } +} + +static GstFlowReturn +gst_rtp_j2k_depay_flush_frame (GstBaseRTPDepayload * depayload) +{ + GstRtpJ2KDepay *rtpj2kdepay; + guint8 end[2]; + guint8 *data; + guint avail; + + GstFlowReturn ret = GST_FLOW_OK; + + rtpj2kdepay = GST_RTP_J2K_DEPAY (depayload); + + /* flush pending tile */ + gst_rtp_j2k_depay_flush_tile (depayload); + + /* last buffer take all data out of the adapter */ + avail = gst_adapter_available (rtpj2kdepay->f_adapter); + if (avail == 0) + goto done; + + if (avail > 2) { + GstBuffer *outbuf; /* take the last bytes of the JPEG 2000 data to see if there is an EOC * marker */ - gst_adapter_copy (rtpj2kdepay->adapter, end, avail - 2, 2); + gst_adapter_copy (rtpj2kdepay->f_adapter, end, avail - 2, 2); if (end[0] != 0xff && end[1] != 0xd9) { GST_DEBUG_OBJECT (rtpj2kdepay, "no EOC marker, adding one"); @@ -211,15 +403,183 @@ gst_rtp_j2k_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) data[0] = 0xff; data[1] = 0xd9; - gst_adapter_push (rtpj2kdepay->adapter, outbuf); + gst_adapter_push (rtpj2kdepay->f_adapter, outbuf); avail += 2; } - outbuf = gst_adapter_take_buffer (rtpj2kdepay->adapter, avail); - GST_DEBUG_OBJECT (rtpj2kdepay, "returning %u bytes", avail); + if (rtpj2kdepay->buffer_list) { + GList *list; + GstBufferList *buflist; + GstBufferListIterator *it; + + GST_DEBUG_OBJECT (rtpj2kdepay, "pushing buffer list of %u bytes", avail); + list = gst_adapter_take_list (rtpj2kdepay->f_adapter, avail); + + buflist = gst_buffer_list_new (); + it = gst_buffer_list_iterate (buflist); + gst_buffer_list_iterator_add_group (it); + gst_buffer_list_iterator_add_list (it, list); + gst_buffer_list_iterator_free (it); + + ret = gst_base_rtp_depayload_push_list (depayload, buflist); + } else { + GST_DEBUG_OBJECT (rtpj2kdepay, "pushing buffer of %u bytes", avail); + outbuf = gst_adapter_take_buffer (rtpj2kdepay->f_adapter, avail); + ret = gst_base_rtp_depayload_push (depayload, outbuf); + } + } else { + GST_WARNING_OBJECT (rtpj2kdepay, "empty packet"); + gst_adapter_clear (rtpj2kdepay->f_adapter); } - return outbuf; + /* we accept any mh_id now */ + rtpj2kdepay->last_mh_id = -1; + + /* reset state */ + rtpj2kdepay->next_frag = 0; + rtpj2kdepay->have_sync = FALSE; + +done: + /* we can't keep headers with mh_id of 0 */ + store_mheader (rtpj2kdepay, 0, NULL); + + return ret; +} + +static GstBuffer * +gst_rtp_j2k_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) +{ + GstRtpJ2KDepay *rtpj2kdepay; + guint8 *payload; + guint MHF, mh_id, frag_offset, tile, payload_len, j2klen; + gint gap; + guint32 rtptime; + + rtpj2kdepay = GST_RTP_J2K_DEPAY (depayload); + + payload = gst_rtp_buffer_get_payload (buf); + payload_len = gst_rtp_buffer_get_payload_len (buf); + + /* we need at least a header */ + if (payload_len < 8) + goto empty_packet; + + rtptime = gst_rtp_buffer_get_timestamp (buf); + + /* new timestamp marks new frame */ + if (rtpj2kdepay->last_rtptime != rtptime) { + rtpj2kdepay->last_rtptime = rtptime; + /* flush pending frame */ + gst_rtp_j2k_depay_flush_frame (depayload); + } + + /* + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |tp |MHF|mh_id|T| priority | tile number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |reserved | fragment offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + MHF = (payload[0] & 0x30) >> 4; + mh_id = (payload[0] & 0xe) >> 1; + + if (rtpj2kdepay->last_mh_id == -1) + rtpj2kdepay->last_mh_id = mh_id; + else if (rtpj2kdepay->last_mh_id != mh_id) + goto wrong_mh_id; + + tile = (payload[2] << 8) | payload[3]; + frag_offset = (payload[5] << 16) | (payload[6] << 8) | payload[7]; + j2klen = payload_len - 8; + + GST_DEBUG_OBJECT (rtpj2kdepay, "MHF %u, tile %u, frag %u, expected %u", MHF, + tile, frag_offset, rtpj2kdepay->next_frag); + + /* calculate the gap between expected frag */ + gap = frag_offset - rtpj2kdepay->next_frag; + /* calculate next frag */ + rtpj2kdepay->next_frag = frag_offset + j2klen; + + if (gap != 0) { + GST_DEBUG_OBJECT (rtpj2kdepay, "discont of %d, clear PU", gap); + /* discont, clear pu adapter and resync */ + gst_rtp_j2k_depay_clear_pu (rtpj2kdepay); + } + + /* check for sync code */ + if (j2klen > 2 && payload[8] == 0xff) { + guint marker = payload[9]; + + /* packets must start with SOC, SOT or SOP */ + switch (marker) { + case J2K_MARKER_SOC: + GST_DEBUG_OBJECT (rtpj2kdepay, "found SOC packet"); + /* flush the previous frame, should have happened when the timestamp + * changed above. */ + gst_rtp_j2k_depay_flush_frame (depayload); + rtpj2kdepay->have_sync = TRUE; + break; + case J2K_MARKER_SOT: + /* flush the previous tile */ + gst_rtp_j2k_depay_flush_tile (depayload); + GST_DEBUG_OBJECT (rtpj2kdepay, "found SOT packet"); + rtpj2kdepay->have_sync = TRUE; + /* we sync on the tile now */ + rtpj2kdepay->last_tile = tile; + break; + case J2K_MARKER_SOP: + GST_DEBUG_OBJECT (rtpj2kdepay, "found SOP packet"); + /* flush the previous PU */ + gst_rtp_j2k_depay_flush_pu (depayload); + if (rtpj2kdepay->last_tile != tile) { + /* wrong tile, we lose sync and we need a new SOT or SOC to regain + * sync. First flush out the previous tile if we have one. */ + if (rtpj2kdepay->last_tile != -1) + gst_rtp_j2k_depay_flush_tile (depayload); + /* now we have no more valid tile and no sync */ + rtpj2kdepay->last_tile = -1; + rtpj2kdepay->have_sync = FALSE; + } else { + rtpj2kdepay->have_sync = TRUE; + } + break; + default: + GST_DEBUG_OBJECT (rtpj2kdepay, "no sync packet 0x%02d", marker); + break; + } + } + + if (rtpj2kdepay->have_sync) { + GstBuffer *pu_frag; + + if (gst_adapter_available (rtpj2kdepay->pu_adapter) == 0) { + /* first part of pu, record state */ + GST_DEBUG_OBJECT (rtpj2kdepay, "first PU"); + rtpj2kdepay->pu_MHF = MHF; + } + /* and push in pu adapter */ + GST_DEBUG_OBJECT (rtpj2kdepay, "push pu of size %u in adapter", j2klen); + pu_frag = gst_rtp_buffer_get_payload_subbuffer (buf, 8, -1); + gst_adapter_push (rtpj2kdepay->pu_adapter, pu_frag); + + if (MHF & 2) { + /* last part of main header received, we can flush it */ + GST_DEBUG_OBJECT (rtpj2kdepay, "header end, flush pu"); + gst_rtp_j2k_depay_flush_pu (depayload); + } + } else { + GST_DEBUG_OBJECT (rtpj2kdepay, "discard packet, no sync"); + } + + /* marker bit finishes the frame */ + if (gst_rtp_buffer_get_marker (buf)) { + GST_DEBUG_OBJECT (rtpj2kdepay, "marker set, last buffer"); + /* then flush frame */ + gst_rtp_j2k_depay_flush_frame (depayload); + } + return NULL; /* ERRORS */ empty_packet: @@ -228,13 +588,52 @@ empty_packet: ("Empty Payload."), (NULL)); return NULL; } -waiting_header: +wrong_mh_id: { - GST_DEBUG_OBJECT (rtpj2kdepay, "we are waiting for a header"); + GST_ELEMENT_WARNING (rtpj2kdepay, STREAM, DECODE, + ("Invalid mh_id %u, expected %u", mh_id, rtpj2kdepay->last_mh_id), + (NULL)); + gst_rtp_j2k_depay_clear_pu (rtpj2kdepay); return NULL; } } +static void +gst_rtp_j2k_depay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpJ2KDepay *rtpj2kdepay; + + rtpj2kdepay = GST_RTP_J2K_DEPAY (object); + + switch (prop_id) { + case PROP_BUFFER_LIST: + rtpj2kdepay->buffer_list = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_j2k_depay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpJ2KDepay *rtpj2kdepay; + + rtpj2kdepay = GST_RTP_J2K_DEPAY (object); + + switch (prop_id) { + case PROP_BUFFER_LIST: + g_value_set_boolean (value, rtpj2kdepay->buffer_list); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static GstStateChangeReturn gst_rtp_j2k_depay_change_state (GstElement * element, GstStateChange transition) { @@ -247,8 +646,7 @@ gst_rtp_j2k_depay_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_adapter_clear (rtpj2kdepay->adapter); - rtpj2kdepay->need_header = TRUE; + gst_rtp_j2k_depay_reset (rtpj2kdepay); break; default: break; @@ -258,7 +656,7 @@ gst_rtp_j2k_depay_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_adapter_clear (rtpj2kdepay->adapter); + gst_rtp_j2k_depay_reset (rtpj2kdepay); break; case GST_STATE_CHANGE_READY_TO_NULL: break; @@ -272,5 +670,5 @@ gboolean gst_rtp_j2k_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpj2kdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_J2K_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_J2K_DEPAY); } diff --git a/gst/rtp/gstrtpj2kdepay.h b/gst/rtp/gstrtpj2kdepay.h index 41120af42e..40373b264b 100644 --- a/gst/rtp/gstrtpj2kdepay.h +++ b/gst/rtp/gstrtpj2kdepay.h @@ -44,9 +44,21 @@ struct _GstRtpJ2KDepay { GstBaseRTPDepayload depayload; - GstAdapter *adapter; - gboolean need_header; + guint64 last_rtptime; + guint last_mh_id; + guint last_tile; + GstBuffer *MH[8]; + + guint pu_MHF; + GstAdapter *pu_adapter; + GstAdapter *t_adapter; + GstAdapter *f_adapter; + + guint next_frag; + gboolean have_sync; + + gboolean buffer_list; gint width, height; }; diff --git a/gst/rtp/gstrtpj2kpay.c b/gst/rtp/gstrtpj2kpay.c index de4462e45b..3734534f8b 100644 --- a/gst/rtp/gstrtpj2kpay.c +++ b/gst/rtp/gstrtpj2kpay.c @@ -73,6 +73,7 @@ typedef enum J2K_MARKER_SOC = 0x4F, J2K_MARKER_SOT = 0x90, J2K_MARKER_SOP = 0x91, + J2K_MARKER_EPH = 0x92, J2K_MARKER_SOD = 0x93, J2K_MARKER_EOC = 0xD9 } RtpJ2KMarker; @@ -86,47 +87,18 @@ enum PROP_LAST }; -/* - * RtpJ2KHeader: - * @tp: type (0 progressive, 1 odd field, 2 even field) - * @MHF: Main Header Flag - * @mh_id: Main Header Identification - * @T: Tile field invalidation flag - * @priority: priority - * @tile number: the tile number of the payload - * @reserved: set to 0 - * @fragment offset: the byte offset of the current payload - * - * 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 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * |tp |MHF|mh_id|T| priority | tile number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * |reserved | fragment offset | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ typedef struct { -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - guint T:1; - guint mh_id:3; - guint MHF:2; - guint tp:2; -#elif G_BYTE_ORDER == G_BIG_ENDIAN guint tp:2; guint MHF:2; guint mh_id:3; guint T:1; -#else -#error "G_BYTE_ORDER should be big or little endian." -#endif guint priority:8; guint tile:16; - guint reserved:8; guint offset:24; } RtpJ2KHeader; -#define HEADER_SIZE sizeof(RtpJ2KHeader) +#define HEADER_SIZE 8 static void gst_rtp_j2k_pay_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); @@ -153,7 +125,7 @@ gst_rtp_j2k_pay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_j2k_pay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP JPEG 2000 payloader", "Codec/Payloader/Network", + "RTP JPEG 2000 payloader", "Codec/Payloader/Network/RTP", "Payload-encodes JPEG 2000 pictures into RTP packets (RFC 5371)", "Wim Taymans "); } @@ -265,13 +237,17 @@ find_pu_end (GstRtpJ2KPay * pay, const guint8 * data, guint size, return offset - 2; cut_sop = TRUE; break; + case J2K_MARKER_EPH: + /* just skip over EPH */ + GST_LOG_OBJECT (pay, "found EPH at %u", offset); + break; default: if (offset >= state->next_sot) { GST_LOG_OBJECT (pay, "reached next SOT at %u", offset); state->bitstream = FALSE; state->force_packet = TRUE; - if (marker == J2K_MARKER_EOC) - /* include EOC */ + if (marker == J2K_MARKER_EOC && state->next_sot + 2 <= size) + /* include EOC but never go past the max size */ return state->next_sot + 2; else return state->next_sot; @@ -388,7 +364,7 @@ gst_rtp_j2k_pay_handle_buffer (GstBaseRTPPayload * basepayload, state.header.T = 1; /* invalid tile */ state.header.priority = 255; /* always 255 for now */ state.header.tile = 0; /* no tile number */ - state.header.reserved = 0; + state.header.offset = 0; /* offset of 0 */ state.bitstream = FALSE; state.n_tiles = 0; state.next_sot = 0; @@ -437,6 +413,11 @@ gst_rtp_j2k_pay_handle_buffer (GstBaseRTPPayload * basepayload, } pos = end; + + /* exit when finished */ + if (pos == size) + break; + /* scan next packetization unit and fill in the header */ end = find_pu_end (pay, data, size, pos, &state); } while (TRUE); @@ -488,14 +469,34 @@ gst_rtp_j2k_pay_handle_buffer (GstBaseRTPPayload * basepayload, gst_rtp_buffer_set_marker (outbuf, TRUE); } - /* copy the header and push the packet */ -#if (G_BYTE_ORDER == G_LITTLE_ENDIAN) - state.header.offset = ((offset & 0x0000FF) << 16) | - ((offset & 0xFF0000) >> 16) | (offset & 0x00FF00); -#else - state.header.offset = offset; -#endif - memcpy (header, &state.header, HEADER_SIZE); + /* + * RtpJ2KHeader: + * @tp: type (0 progressive, 1 odd field, 2 even field) + * @MHF: Main Header Flag + * @mh_id: Main Header Identification + * @T: Tile field invalidation flag + * @priority: priority + * @tile number: the tile number of the payload + * @reserved: set to 0 + * @fragment offset: the byte offset of the current payload + * + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |tp |MHF|mh_id|T| priority | tile number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |reserved | fragment offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + header[0] = (state.header.tp << 6) | (state.header.MHF << 4) | + (state.header.mh_id << 1) | state.header.T; + header[1] = state.header.priority; + header[2] = state.header.tile >> 8; + header[3] = state.header.tile & 0xff; + header[4] = 0; + header[5] = state.header.offset >> 16; + header[6] = (state.header.offset >> 8) & 0xff; + header[7] = state.header.offset & 0xff; if (pay->buffer_list) { GstBuffer *paybuf; @@ -579,6 +580,6 @@ gst_rtp_j2k_pay_get_property (GObject * object, guint prop_id, gboolean gst_rtp_j2k_pay_plugin_init (GstPlugin * plugin) { - return gst_element_register (plugin, "rtpj2kpay", GST_RANK_NONE, + return gst_element_register (plugin, "rtpj2kpay", GST_RANK_SECONDARY, GST_TYPE_RTP_J2K_PAY); } diff --git a/gst/rtp/gstrtpjpegdepay.c b/gst/rtp/gstrtpjpegdepay.c index f2df87f7b3..5ab88fb7bf 100644 --- a/gst/rtp/gstrtpjpegdepay.c +++ b/gst/rtp/gstrtpjpegdepay.c @@ -90,7 +90,7 @@ gst_rtp_jpeg_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_jpeg_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP JPEG depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts JPEG video from RTP packets (RFC 2435)", "Wim Taymans "); } @@ -460,15 +460,23 @@ gst_rtp_jpeg_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) if (media_attr) { GValue src = { 0 }; GValue dest = { 0 }; + gchar *s; + + /* canonicalise floating point string so we can handle framerate strings + * in the form "24.930" or "24,930" irrespective of the current locale */ + s = g_strdup (media_attr); + g_strdelimit (s, ",", '.'); /* convert the float to a fraction */ g_value_init (&src, G_TYPE_DOUBLE); - g_value_set_double (&src, atof (media_attr)); + g_value_set_double (&src, g_ascii_strtod (s, NULL)); g_value_init (&dest, GST_TYPE_FRACTION); g_value_transform (&src, &dest); rtpjpegdepay->frate_num = gst_value_get_fraction_numerator (&dest); rtpjpegdepay->frate_denom = gst_value_get_fraction_denominator (&dest); + + g_free (s); } return TRUE; @@ -747,5 +755,5 @@ gboolean gst_rtp_jpeg_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpjpegdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_JPEG_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_JPEG_DEPAY); } diff --git a/gst/rtp/gstrtpjpegpay.c b/gst/rtp/gstrtpjpegpay.c index 3d09c698ff..fc4096d5c2 100644 --- a/gst/rtp/gstrtpjpegpay.c +++ b/gst/rtp/gstrtpjpegpay.c @@ -248,7 +248,7 @@ gst_rtp_jpeg_pay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_jpeg_pay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP JPEG payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payload-encodes JPEG pictures into RTP packets (RFC 2435)", "Axis Communications "); } @@ -926,6 +926,6 @@ gst_rtp_jpeg_pay_get_property (GObject * object, guint prop_id, gboolean gst_rtp_jpeg_pay_plugin_init (GstPlugin * plugin) { - return gst_element_register (plugin, "rtpjpegpay", GST_RANK_NONE, + return gst_element_register (plugin, "rtpjpegpay", GST_RANK_SECONDARY, GST_TYPE_RTP_JPEG_PAY); } diff --git a/gst/rtp/gstrtpmp1sdepay.c b/gst/rtp/gstrtpmp1sdepay.c index 190bfca65c..4887c4cdea 100644 --- a/gst/rtp/gstrtpmp1sdepay.c +++ b/gst/rtp/gstrtpmp1sdepay.c @@ -81,7 +81,7 @@ gst_rtp_mp1s_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_mp1s_depay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP MPEG1 System Stream depayloader", "Codec/Depayloader/Network", + "RTP MPEG1 System Stream depayloader", "Codec/Depayloader/Network/RTP", "Extracts MPEG1 System Streams from RTP packets (RFC 3555)", "Wim Taymans "); } @@ -141,5 +141,5 @@ gboolean gst_rtp_mp1s_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpmp1sdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_MP1S_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_MP1S_DEPAY); } diff --git a/gst/rtp/gstrtpmp2tdepay.c b/gst/rtp/gstrtpmp2tdepay.c index 4f9ba8933f..fe6c5dee2a 100644 --- a/gst/rtp/gstrtpmp2tdepay.c +++ b/gst/rtp/gstrtpmp2tdepay.c @@ -92,7 +92,7 @@ gst_rtp_mp2t_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_mp2t_depay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP MPEG Transport Stream depayloader", "Codec/Depayloader/Network", + "RTP MPEG Transport Stream depayloader", "Codec/Depayloader/Network/RTP", "Extracts MPEG2 TS from RTP packets (RFC 2250)", "Wim Taymans , " "Thijs Vermeir "); @@ -223,5 +223,5 @@ gboolean gst_rtp_mp2t_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpmp2tdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_MP2T_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_MP2T_DEPAY); } diff --git a/gst/rtp/gstrtpmp2tpay.c b/gst/rtp/gstrtpmp2tpay.c index 41b450e3ca..3ddb5cb793 100644 --- a/gst/rtp/gstrtpmp2tpay.c +++ b/gst/rtp/gstrtpmp2tpay.c @@ -65,7 +65,7 @@ gst_rtp_mp2t_pay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_mp2t_pay_src_template)); gst_element_class_set_details_simple (element_class, - "RTP MPEG2 Transport Stream payloader", "Codec/Payloader/Network", + "RTP MPEG2 Transport Stream payloader", "Codec/Payloader/Network/RTP", "Payload-encodes MPEG2 TS into RTP packets (RFC 2250)", "Wim Taymans "); } @@ -202,5 +202,5 @@ gboolean gst_rtp_mp2t_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpmp2tpay", - GST_RANK_NONE, GST_TYPE_RTP_MP2T_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_MP2T_PAY); } diff --git a/gst/rtp/gstrtpmp4adepay.c b/gst/rtp/gstrtpmp4adepay.c index f913a67cac..18ebfd2190 100644 --- a/gst/rtp/gstrtpmp4adepay.c +++ b/gst/rtp/gstrtpmp4adepay.c @@ -21,6 +21,7 @@ # include "config.h" #endif +#include #include #include @@ -80,7 +81,7 @@ gst_rtp_mp4a_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_mp4a_depay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP MPEG4 audio depayloader", "Codec/Depayloader/Network", + "RTP MPEG4 audio depayloader", "Codec/Depayloader/Network/RTP", "Extracts MPEG4 audio from RTP packets (RFC 3016)", "Nokia Corporation (contact ), " "Wim Taymans "); @@ -128,6 +129,10 @@ gst_rtp_mp4a_depay_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } +static const guint aac_sample_rates[] = { 96000, 88200, 64000, 48000, + 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000 +}; + static gboolean gst_rtp_mp4a_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) { @@ -165,10 +170,9 @@ gst_rtp_mp4a_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) guint8 *data; guint size; gint i; - guint sr_idx; - static const guint aac_sample_rates[] = { 96000, 88200, 64000, 48000, - 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000 - }; + guint32 rate = 0; + guint8 obj_type = 0, sr_idx = 0, channels = 0; + GstBitReader br; buffer = gst_value_get_buffer (&v); gst_buffer_ref (buffer); @@ -210,22 +214,68 @@ gst_rtp_mp4a_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) for (i = 0; i < size; i++) { data[i] = ((data[i + 1] & 1) << 7) | ((data[i + 2] & 0xfe) >> 1); } - - /* grab and set sampling rate */ - sr_idx = ((data[0] & 0x07) << 1) | ((data[1] & 0x80) >> 7); - if (sr_idx < G_N_ELEMENTS (aac_sample_rates)) { - gst_caps_set_simple (srccaps, - "rate", G_TYPE_INT, (gint) aac_sample_rates[sr_idx], NULL); - GST_DEBUG_OBJECT (depayload, "sampling rate from stream-config %u", - aac_sample_rates[sr_idx]); - } else { - GST_WARNING_OBJECT (depayload, "Invalid sample rate index %u", sr_idx); - } - /* ignore remaining bit, we're only interested in full bytes */ GST_BUFFER_SIZE (buffer) = size; + gst_bit_reader_init (&br, data, size); + + /* any object type is fine, we need to copy it to the profile-level-id field. */ + if (!gst_bit_reader_get_bits_uint8 (&br, &obj_type, 5)) + goto bad_config; + if (obj_type == 0) { + GST_WARNING_OBJECT (depayload, "invalid object type 0"); + goto bad_config; + } + + if (!gst_bit_reader_get_bits_uint8 (&br, &sr_idx, 4)) + goto bad_config; + if (sr_idx > 12 && sr_idx != 15) { + GST_WARNING_OBJECT (depayload, "invalid sample rate index %d", sr_idx); + goto bad_config; + } + GST_LOG_OBJECT (rtpmp4adepay, "sample rate index %u", sr_idx); + + if (!gst_bit_reader_get_bits_uint8 (&br, &channels, 4)) + goto bad_config; + if (channels > 7) { + GST_WARNING_OBJECT (depayload, "invalid channels %u", (guint) channels); + goto bad_config; + } + + /* rtp rate depends on sampling rate of the audio */ + if (sr_idx == 15) { + /* index of 15 means we get the rate in the next 24 bits */ + if (!gst_bit_reader_get_bits_uint32 (&br, &rate, 24)) + goto bad_config; + } else { + /* else use the rate from the table */ + rate = aac_sample_rates[sr_idx]; + } + + rtpmp4adepay->frame_len = 1024; + + switch (obj_type) { + case 1: + case 2: + case 3: + case 4: + case 6: + case 7: + { + guint8 frameLenFlag = 0; + + if (gst_bit_reader_get_bits_uint8 (&br, &frameLenFlag, 1)) + if (frameLenFlag) + rtpmp4adepay->frame_len = 960; + break; + } + default: + break; + } + gst_caps_set_simple (srccaps, + "channels", G_TYPE_INT, (gint) channels, + "rate", G_TYPE_INT, (gint) rate, "codec_data", GST_TYPE_BUFFER, buffer, NULL); gst_buffer_unref (buffer); } else { @@ -254,6 +304,7 @@ gst_rtp_mp4a_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) outbuf = gst_rtp_buffer_get_payload_buffer (buf); + gst_buffer_copy_metadata (outbuf, buf, GST_BUFFER_COPY_TIMESTAMPS); gst_adapter_push (rtpmp4adepay->adapter, outbuf); /* RTP marker bit indicates the last packet of the AudioMuxElement => create @@ -263,8 +314,11 @@ gst_rtp_mp4a_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) guint i; guint8 *data; guint pos; + GstClockTime timestamp; + guint offset; avail = gst_adapter_available (rtpmp4adepay->adapter); + timestamp = gst_adapter_prev_timestamp (rtpmp4adepay->adapter, NULL); GST_LOG_OBJECT (rtpmp4adepay, "have marker and %u available", avail); @@ -272,18 +326,16 @@ gst_rtp_mp4a_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) data = GST_BUFFER_DATA (outbuf); /* position in data we are at */ pos = 0; + /* timestamp offset */ + offset = 0; /* looping through the number of sub-frames in the audio payload */ for (i = 0; i <= rtpmp4adepay->numSubFrames; i++) { /* determine payload length and set buffer data pointer accordingly */ guint skip; guint data_len; - guint32 timestamp; - GstBuffer *tmp = NULL; - timestamp = gst_rtp_buffer_get_timestamp (buf); - /* each subframe starts with a variable length encoding */ data_len = 0; for (skip = 0; skip < avail; skip++) { @@ -314,13 +366,19 @@ gst_rtp_mp4a_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) data += skip; avail -= skip; - gst_buffer_set_caps (tmp, GST_PAD_CAPS (depayload->srcpad)); + if (offset > 0 && timestamp != -1 && depayload->clock_rate != 0) { + timestamp += + gst_util_uint64_scale_int (offset, GST_SECOND, + depayload->clock_rate); + } - /* only apply the timestamp for the first buffer. Based on gstrtpmp4gdepay.c */ - if (i == 0) - gst_base_rtp_depayload_push_ts (depayload, timestamp, tmp); - else - gst_base_rtp_depayload_push (depayload, tmp); + GST_BUFFER_TIMESTAMP (tmp) = timestamp; + gst_base_rtp_depayload_push (depayload, tmp); + + /* calculate offsets for next buffers */ + if (rtpmp4adepay->frame_len) { + offset += rtpmp4adepay->frame_len; + } } /* just a check that lengths match */ @@ -356,6 +414,8 @@ gst_rtp_mp4a_depay_change_state (GstElement * element, switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: gst_adapter_clear (rtpmp4adepay->adapter); + rtpmp4adepay->frame_len = 0; + rtpmp4adepay->numSubFrames = 0; break; default: break; @@ -374,5 +434,5 @@ gboolean gst_rtp_mp4a_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpmp4adepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_MP4A_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_MP4A_DEPAY); } diff --git a/gst/rtp/gstrtpmp4adepay.h b/gst/rtp/gstrtpmp4adepay.h index 16c20b7f7a..33622fcaed 100644 --- a/gst/rtp/gstrtpmp4adepay.h +++ b/gst/rtp/gstrtpmp4adepay.h @@ -44,6 +44,7 @@ struct _GstRtpMP4ADepay GstBaseRTPDepayload depayload; GstAdapter *adapter; guint8 numSubFrames; + guint frame_len; }; struct _GstRtpMP4ADepayClass diff --git a/gst/rtp/gstrtpmp4apay.c b/gst/rtp/gstrtpmp4apay.c index b30b3c9558..70fdbed7a5 100644 --- a/gst/rtp/gstrtpmp4apay.c +++ b/gst/rtp/gstrtpmp4apay.c @@ -77,7 +77,7 @@ GST_BOILERPLATE (GstRtpMP4APay, gst_rtp_mp4a_pay, GstBaseRTPPayload, gst_static_pad_template_get (&gst_rtp_mp4a_pay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP MPEG4 audio payloader", "Codec/Payloader/Network", + "RTP MPEG4 audio payloader", "Codec/Payloader/Network/RTP", "Payload MPEG4 audio as RTP packets (RFC 3016)", "Wim Taymans "); } @@ -433,5 +433,5 @@ gboolean gst_rtp_mp4a_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpmp4apay", - GST_RANK_NONE, GST_TYPE_RTP_MP4A_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_MP4A_PAY); } diff --git a/gst/rtp/gstrtpmp4gdepay.c b/gst/rtp/gstrtpmp4gdepay.c index 8307becca6..02ac8d681a 100644 --- a/gst/rtp/gstrtpmp4gdepay.c +++ b/gst/rtp/gstrtpmp4gdepay.c @@ -135,6 +135,8 @@ static gboolean gst_rtp_mp4g_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps); static GstBuffer *gst_rtp_mp4g_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf); +static gboolean gst_rtp_mp4g_depay_handle_event (GstBaseRTPDepayload * filter, + GstEvent * event); static GstStateChangeReturn gst_rtp_mp4g_depay_change_state (GstElement * element, GstStateChange transition); @@ -151,7 +153,7 @@ gst_rtp_mp4g_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_mp4g_depay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP MPEG4 ES depayloader", "Codec/Depayloader/Network", + "RTP MPEG4 ES depayloader", "Codec/Depayloader/Network/RTP", "Extracts MPEG4 elementary streams from RTP packets (RFC 3640)", "Wim Taymans "); } @@ -173,6 +175,7 @@ gst_rtp_mp4g_depay_class_init (GstRtpMP4GDepayClass * klass) gstbasertpdepayload_class->process = gst_rtp_mp4g_depay_process; gstbasertpdepayload_class->set_caps = gst_rtp_mp4g_depay_setcaps; + gstbasertpdepayload_class->handle_event = gst_rtp_mp4g_depay_handle_event; GST_DEBUG_CATEGORY_INIT (rtpmp4gdepay_debug, "rtpmp4gdepay", 0, "MP4-generic RTP Depayloader"); @@ -315,6 +318,18 @@ gst_rtp_mp4g_depay_clear_queue (GstRtpMP4GDepay * rtpmp4gdepay) gst_buffer_unref (outbuf); } +static void +gst_rtp_mp4g_depay_reset (GstRtpMP4GDepay * rtpmp4gdepay) +{ + gst_adapter_clear (rtpmp4gdepay->adapter); + rtpmp4gdepay->max_AU_index = -1; + rtpmp4gdepay->next_AU_index = -1; + rtpmp4gdepay->prev_AU_index = -1; + rtpmp4gdepay->prev_rtptime = -1; + rtpmp4gdepay->last_AU_index = -1; + gst_rtp_mp4g_depay_clear_queue (rtpmp4gdepay); +} + static void gst_rtp_mp4g_depay_flush_queue (GstRtpMP4GDepay * rtpmp4gdepay) { @@ -699,6 +714,28 @@ short_payload: } } +static gboolean +gst_rtp_mp4g_depay_handle_event (GstBaseRTPDepayload * filter, GstEvent * event) +{ + gboolean ret; + GstRtpMP4GDepay *rtpmp4gdepay; + + rtpmp4gdepay = GST_RTP_MP4G_DEPAY (filter); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_rtp_mp4g_depay_reset (rtpmp4gdepay); + break; + default: + break; + } + + ret = + GST_BASE_RTP_DEPAYLOAD_CLASS (parent_class)->handle_event (filter, event); + + return ret; +} + static GstStateChangeReturn gst_rtp_mp4g_depay_change_state (GstElement * element, GstStateChange transition) @@ -710,13 +747,7 @@ gst_rtp_mp4g_depay_change_state (GstElement * element, switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_adapter_clear (rtpmp4gdepay->adapter); - rtpmp4gdepay->max_AU_index = -1; - rtpmp4gdepay->next_AU_index = -1; - rtpmp4gdepay->prev_AU_index = -1; - rtpmp4gdepay->prev_rtptime = -1; - rtpmp4gdepay->last_AU_index = -1; - rtpmp4gdepay->prev_AU_num = -1; + gst_rtp_mp4g_depay_reset (rtpmp4gdepay); break; default: break; @@ -726,8 +757,7 @@ gst_rtp_mp4g_depay_change_state (GstElement * element, switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_adapter_clear (rtpmp4gdepay->adapter); - gst_rtp_mp4g_depay_clear_queue (rtpmp4gdepay); + gst_rtp_mp4g_depay_reset (rtpmp4gdepay); break; default: break; @@ -739,5 +769,5 @@ gboolean gst_rtp_mp4g_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpmp4gdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_MP4G_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_MP4G_DEPAY); } diff --git a/gst/rtp/gstrtpmp4gpay.c b/gst/rtp/gstrtpmp4gpay.c index 81e6f9b86d..c095064062 100644 --- a/gst/rtp/gstrtpmp4gpay.c +++ b/gst/rtp/gstrtpmp4gpay.c @@ -82,6 +82,7 @@ static gboolean gst_rtp_mp4g_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps); static GstFlowReturn gst_rtp_mp4g_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer); +static gboolean gst_rtp_mp4g_pay_handle_event (GstPad * pad, GstEvent * event); GST_BOILERPLATE (GstRtpMP4GPay, gst_rtp_mp4g_pay, GstBaseRTPPayload, GST_TYPE_BASE_RTP_PAYLOAD) @@ -96,7 +97,7 @@ GST_BOILERPLATE (GstRtpMP4GPay, gst_rtp_mp4g_pay, GstBaseRTPPayload, gst_static_pad_template_get (&gst_rtp_mp4g_pay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP MPEG4 ES payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payload MPEG4 elementary streams as RTP packets (RFC 3640)", "Wim Taymans "); } @@ -118,6 +119,7 @@ gst_rtp_mp4g_pay_class_init (GstRtpMP4GPayClass * klass) gstbasertppayload_class->set_caps = gst_rtp_mp4g_pay_setcaps; gstbasertppayload_class->handle_buffer = gst_rtp_mp4g_pay_handle_buffer; + gstbasertppayload_class->handle_event = gst_rtp_mp4g_pay_handle_event; GST_DEBUG_CATEGORY_INIT (rtpmp4gpay_debug, "rtpmp4gpay", 0, "MP4-generic RTP Payloader"); @@ -135,6 +137,13 @@ gst_rtp_mp4g_pay_reset (GstRtpMP4GPay * rtpmp4gpay) GST_DEBUG_OBJECT (rtpmp4gpay, "reset"); gst_adapter_clear (rtpmp4gpay->adapter); + rtpmp4gpay->offset = 0; +} + +static void +gst_rtp_mp4g_pay_cleanup (GstRtpMP4GPay * rtpmp4gpay) +{ + gst_rtp_mp4g_pay_reset (rtpmp4gpay); g_free (rtpmp4gpay->params); rtpmp4gpay->params = NULL; @@ -150,7 +159,6 @@ gst_rtp_mp4g_pay_reset (GstRtpMP4GPay * rtpmp4gpay) rtpmp4gpay->mode = NULL; rtpmp4gpay->frame_len = 0; - rtpmp4gpay->offset = 0; } static void @@ -160,7 +168,7 @@ gst_rtp_mp4g_pay_finalize (GObject * object) rtpmp4gpay = GST_RTP_MP4G_PAY (object); - gst_rtp_mp4g_pay_reset (rtpmp4gpay); + gst_rtp_mp4g_pay_cleanup (rtpmp4gpay); g_object_unref (rtpmp4gpay->adapter); rtpmp4gpay->adapter = NULL; @@ -561,6 +569,35 @@ gst_rtp_mp4g_pay_handle_buffer (GstBaseRTPPayload * basepayload, return gst_rtp_mp4g_pay_flush (rtpmp4gpay); } +static gboolean +gst_rtp_mp4g_pay_handle_event (GstPad * pad, GstEvent * event) +{ + GstRtpMP4GPay *rtpmp4gpay; + + rtpmp4gpay = GST_RTP_MP4G_PAY (gst_pad_get_parent (pad)); + + GST_DEBUG ("Got event: %s", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT: + case GST_EVENT_EOS: + /* This flush call makes sure that the last buffer is always pushed + * to the base payloader */ + gst_rtp_mp4g_pay_flush (rtpmp4gpay); + break; + case GST_EVENT_FLUSH_STOP: + gst_rtp_mp4g_pay_reset (rtpmp4gpay); + break; + default: + break; + } + + g_object_unref (rtpmp4gpay); + + /* let parent handle event too */ + return FALSE; +} + static GstStateChangeReturn gst_rtp_mp4g_pay_change_state (GstElement * element, GstStateChange transition) { @@ -571,7 +608,7 @@ gst_rtp_mp4g_pay_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_rtp_mp4g_pay_reset (rtpmp4gpay); + gst_rtp_mp4g_pay_cleanup (rtpmp4gpay); break; default: break; @@ -579,6 +616,14 @@ gst_rtp_mp4g_pay_change_state (GstElement * element, GstStateChange transition) ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_mp4g_pay_cleanup (rtpmp4gpay); + break; + default: + break; + } + return ret; } @@ -586,5 +631,5 @@ gboolean gst_rtp_mp4g_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpmp4gpay", - GST_RANK_NONE, GST_TYPE_RTP_MP4G_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_MP4G_PAY); } diff --git a/gst/rtp/gstrtpmp4vdepay.c b/gst/rtp/gstrtpmp4vdepay.c index c1c911afe8..ce18cecdd5 100644 --- a/gst/rtp/gstrtpmp4vdepay.c +++ b/gst/rtp/gstrtpmp4vdepay.c @@ -78,7 +78,7 @@ gst_rtp_mp4v_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_mp4v_depay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP MPEG4 video depayloader", "Codec/Depayloader/Network", + "RTP MPEG4 video depayloader", "Codec/Depayloader/Network/RTP", "Extracts MPEG4 video from RTP packets (RFC 3016)", "Wim Taymans "); } @@ -227,5 +227,5 @@ gboolean gst_rtp_mp4v_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpmp4vdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_MP4V_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_MP4V_DEPAY); } diff --git a/gst/rtp/gstrtpmp4vpay.c b/gst/rtp/gstrtpmp4vpay.c index 07fd84a082..811e1b4a58 100644 --- a/gst/rtp/gstrtpmp4vpay.c +++ b/gst/rtp/gstrtpmp4vpay.c @@ -31,11 +31,11 @@ GST_DEBUG_CATEGORY_STATIC (rtpmp4vpay_debug); #define GST_CAT_DEFAULT (rtpmp4vpay_debug) static GstStaticPadTemplate gst_rtp_mp4v_pay_sink_template = -GST_STATIC_PAD_TEMPLATE ("sink", + GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/mpeg," - "mpegversion=(int) 4," "systemstream=(boolean)false") + "mpegversion=(int) 4," "systemstream=(boolean)false;" "video/x-xvid") ); static GstStaticPadTemplate gst_rtp_mp4v_pay_src_template = @@ -78,7 +78,7 @@ static gboolean gst_rtp_mp4v_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps); static GstFlowReturn gst_rtp_mp4v_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer); -static gboolean gst_rtp_mp4v_pay_event (GstPad * pad, GstEvent * event); +static gboolean gst_rtp_mp4v_pay_handle_event (GstPad * pad, GstEvent * event); GST_BOILERPLATE (GstRtpMP4VPay, gst_rtp_mp4v_pay, GstBaseRTPPayload, GST_TYPE_BASE_RTP_PAYLOAD) @@ -93,7 +93,7 @@ GST_BOILERPLATE (GstRtpMP4VPay, gst_rtp_mp4v_pay, GstBaseRTPPayload, gst_static_pad_template_get (&gst_rtp_mp4v_pay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP MPEG4 Video payloader", "Codec/Payloader/Network", + "RTP MPEG4 Video payloader", "Codec/Payloader/Network/RTP", "Payload MPEG-4 video as RTP packets (RFC 3016)", "Wim Taymans "); } @@ -132,6 +132,7 @@ gst_rtp_mp4v_pay_class_init (GstRtpMP4VPayClass * klass) gstbasertppayload_class->set_caps = gst_rtp_mp4v_pay_setcaps; gstbasertppayload_class->handle_buffer = gst_rtp_mp4v_pay_handle_buffer; + gstbasertppayload_class->handle_event = gst_rtp_mp4v_pay_handle_event; GST_DEBUG_CATEGORY_INIT (rtpmp4vpay_debug, "rtpmp4vpay", 0, "MP4 video RTP Payloader"); @@ -154,9 +155,6 @@ gst_rtp_mp4v_pay_init (GstRtpMP4VPay * rtpmp4vpay, GstRtpMP4VPayClass * klass) rtpmp4vpay->config = NULL; sinkpad = GST_BASE_RTP_PAYLOAD_SINKPAD (rtpmp4vpay); - - rtpmp4vpay->old_event_func = sinkpad->eventfunc; - gst_pad_set_event_function (sinkpad, gst_rtp_mp4v_pay_event); } static void @@ -596,10 +594,9 @@ gst_rtp_mp4v_pay_handle_buffer (GstBaseRTPPayload * basepayload, } static gboolean -gst_rtp_mp4v_pay_event (GstPad * pad, GstEvent * event) +gst_rtp_mp4v_pay_handle_event (GstPad * pad, GstEvent * event) { GstRtpMP4VPay *rtpmp4vpay; - gboolean ret; rtpmp4vpay = GST_RTP_MP4V_PAY (gst_pad_get_parent (pad)); @@ -619,11 +616,10 @@ gst_rtp_mp4v_pay_event (GstPad * pad, GstEvent * event) break; } - ret = rtpmp4vpay->old_event_func (pad, event); - g_object_unref (rtpmp4vpay); - return ret; + /* let parent handle event too */ + return FALSE; } static void @@ -676,5 +672,5 @@ gboolean gst_rtp_mp4v_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpmp4vpay", - GST_RANK_NONE, GST_TYPE_RTP_MP4V_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_MP4V_PAY); } diff --git a/gst/rtp/gstrtpmp4vpay.h b/gst/rtp/gstrtpmp4vpay.h index 7d8bac2407..0f8ab14f6f 100644 --- a/gst/rtp/gstrtpmp4vpay.h +++ b/gst/rtp/gstrtpmp4vpay.h @@ -60,8 +60,6 @@ struct _GstRtpMP4VPay * payloader */ guint config_interval; GstClockTime last_config; - - GstPadEventFunction old_event_func; }; struct _GstRtpMP4VPayClass diff --git a/gst/rtp/gstrtpmpadepay.c b/gst/rtp/gstrtpmpadepay.c index 49c97e89c8..a76a7f39e6 100644 --- a/gst/rtp/gstrtpmpadepay.c +++ b/gst/rtp/gstrtpmpadepay.c @@ -69,7 +69,7 @@ gst_rtp_mpa_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_mpa_depay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP MPEG audio depayloader", "Codec/Depayloader/Network", + "RTP MPEG audio depayloader", "Codec/Depayloader/Network/RTP", "Extracts MPEG audio from RTP packets (RFC 2038)", "Wim Taymans "); } @@ -179,5 +179,5 @@ gboolean gst_rtp_mpa_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpmpadepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_MPA_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_MPA_DEPAY); } diff --git a/gst/rtp/gstrtpmpapay.c b/gst/rtp/gstrtpmpapay.c index f57d628ead..f685ef271d 100644 --- a/gst/rtp/gstrtpmpapay.c +++ b/gst/rtp/gstrtpmpapay.c @@ -27,6 +27,9 @@ #include "gstrtpmpapay.h" +GST_DEBUG_CATEGORY_STATIC (rtpmpapay_debug); +#define GST_CAT_DEFAULT (rtpmpapay_debug) + static GstStaticPadTemplate gst_rtp_mpa_pay_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -50,8 +53,13 @@ static GstStaticPadTemplate gst_rtp_mpa_pay_src_template = static void gst_rtp_mpa_pay_finalize (GObject * object); +static GstStateChangeReturn gst_rtp_mpa_pay_change_state (GstElement * element, + GstStateChange transition); + static gboolean gst_rtp_mpa_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps); +static gboolean gst_rtp_mpa_pay_handle_event (GstPad * pad, GstEvent * event); +static GstFlowReturn gst_rtp_mpa_pay_flush (GstRtpMPAPay * rtpmpapay); static GstFlowReturn gst_rtp_mpa_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer); @@ -68,7 +76,7 @@ GST_BOILERPLATE (GstRtpMPAPay, gst_rtp_mpa_pay, GstBaseRTPPayload, gst_static_pad_template_get (&gst_rtp_mpa_pay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP MPEG audio payloader", "Codec/Payloader/Network", + "RTP MPEG audio payloader", "Codec/Payloader/Network/RTP", "Payload MPEG audio as RTP packets (RFC 2038)", "Wim Taymans "); } @@ -77,15 +85,23 @@ static void gst_rtp_mpa_pay_class_init (GstRtpMPAPayClass * klass) { GObjectClass *gobject_class; + GstElementClass *gstelement_class; GstBaseRTPPayloadClass *gstbasertppayload_class; gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass; gobject_class->finalize = gst_rtp_mpa_pay_finalize; + gstelement_class->change_state = gst_rtp_mpa_pay_change_state; + gstbasertppayload_class->set_caps = gst_rtp_mpa_pay_setcaps; + gstbasertppayload_class->handle_event = gst_rtp_mpa_pay_handle_event; gstbasertppayload_class->handle_buffer = gst_rtp_mpa_pay_handle_buffer; + + GST_DEBUG_CATEGORY_INIT (rtpmpapay_debug, "rtpmpapay", 0, + "MPEG Audio RTP Depayloader"); } static void @@ -107,6 +123,15 @@ gst_rtp_mpa_pay_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } +static void +gst_rtp_mpa_pay_reset (GstRtpMPAPay * pay) +{ + pay->first_ts = -1; + pay->duration = 0; + gst_adapter_clear (pay->adapter); + GST_DEBUG_OBJECT (pay, "reset depayloader"); +} + static gboolean gst_rtp_mpa_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) { @@ -118,6 +143,31 @@ gst_rtp_mpa_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) return res; } +static gboolean +gst_rtp_mpa_pay_handle_event (GstPad * pad, GstEvent * event) +{ + GstRtpMPAPay *rtpmpapay; + + rtpmpapay = GST_RTP_MPA_PAY (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* make sure we push the last packets in the adapter on EOS */ + gst_rtp_mpa_pay_flush (rtpmpapay); + break; + case GST_EVENT_FLUSH_STOP: + gst_rtp_mpa_pay_reset (rtpmpapay); + break; + default: + break; + } + + gst_object_unref (rtpmpapay); + + /* FALSE to let the parent handle the event as well */ + return FALSE; +} + static GstFlowReturn gst_rtp_mpa_pay_flush (GstRtpMPAPay * rtpmpapay) { @@ -143,7 +193,7 @@ gst_rtp_mpa_pay_flush (GstRtpMPAPay * rtpmpapay) guint payload_len; guint packet_len; - /* this will be the total lenght of the packet */ + /* this will be the total length of the packet */ packet_len = gst_rtp_buffer_calc_packet_len (4 + avail, 0, 0); /* fill one MTU or all available bytes */ @@ -198,18 +248,20 @@ gst_rtp_mpa_pay_handle_buffer (GstBaseRTPPayload * basepayload, GstFlowReturn ret; guint size, avail; guint packet_len; - GstClockTime duration; + GstClockTime duration, timestamp; rtpmpapay = GST_RTP_MPA_PAY (basepayload); size = GST_BUFFER_SIZE (buffer); duration = GST_BUFFER_DURATION (buffer); + timestamp = GST_BUFFER_TIMESTAMP (buffer); + + if (GST_BUFFER_IS_DISCONT (buffer)) { + GST_DEBUG_OBJECT (rtpmpapay, "DISCONT"); + gst_rtp_mpa_pay_reset (rtpmpapay); + } avail = gst_adapter_available (rtpmpapay->adapter); - if (avail == 0) { - rtpmpapay->first_ts = GST_BUFFER_TIMESTAMP (buffer); - rtpmpapay->duration = 0; - } /* get packet length of previous data and this new data, * payload length includes a 4 byte header */ @@ -220,15 +272,50 @@ gst_rtp_mpa_pay_handle_buffer (GstBaseRTPPayload * basepayload, if (gst_basertppayload_is_filled (basepayload, packet_len, rtpmpapay->duration + duration)) { ret = gst_rtp_mpa_pay_flush (rtpmpapay); - rtpmpapay->first_ts = GST_BUFFER_TIMESTAMP (buffer); - rtpmpapay->duration = 0; + avail = 0; } else { ret = GST_FLOW_OK; } - gst_adapter_push (rtpmpapay->adapter, buffer); - rtpmpapay->duration += duration; + if (avail == 0) { + GST_DEBUG_OBJECT (rtpmpapay, + "first packet, save timestamp %" GST_TIME_FORMAT, + GST_TIME_ARGS (timestamp)); + rtpmpapay->first_ts = timestamp; + rtpmpapay->duration = 0; + } + gst_adapter_push (rtpmpapay->adapter, buffer); + rtpmpapay->duration = duration; + + return ret; +} + +static GstStateChangeReturn +gst_rtp_mpa_pay_change_state (GstElement * element, GstStateChange transition) +{ + GstRtpMPAPay *rtpmpapay; + GstStateChangeReturn ret; + + rtpmpapay = GST_RTP_MPA_PAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_mpa_pay_reset (rtpmpapay); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_mpa_pay_reset (rtpmpapay); + break; + default: + break; + } return ret; } @@ -236,5 +323,5 @@ gboolean gst_rtp_mpa_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpmpapay", - GST_RANK_NONE, GST_TYPE_RTP_MPA_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_MPA_PAY); } diff --git a/gst/rtp/gstrtpmparobustdepay.c b/gst/rtp/gstrtpmparobustdepay.c index b250f4137d..0f3c04959c 100644 --- a/gst/rtp/gstrtpmparobustdepay.c +++ b/gst/rtp/gstrtpmparobustdepay.c @@ -91,7 +91,7 @@ gst_rtp_mpa_robust_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_mpa_robust_depay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP MPEG audio depayloader", "Codec/Depayloader/Network", + "RTP MPEG audio depayloader", "Codec/Depayloader/Network/RTP", "Extracts MPEG audio from RTP packets (RFC 5219)", "Mark Nauwelaerts "); } @@ -788,5 +788,5 @@ gboolean gst_rtp_mpa_robust_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpmparobustdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_MPA_ROBUST_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_MPA_ROBUST_DEPAY); } diff --git a/gst/rtp/gstrtpmpvdepay.c b/gst/rtp/gstrtpmpvdepay.c index bb377eddfe..f9786048d4 100644 --- a/gst/rtp/gstrtpmpvdepay.c +++ b/gst/rtp/gstrtpmpvdepay.c @@ -72,7 +72,7 @@ gst_rtp_mpv_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_mpv_depay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP MPEG video depayloader", "Codec/Depayloader/Network", + "RTP MPEG video depayloader", "Codec/Depayloader/Network/RTP", "Extracts MPEG video from RTP packets (RFC 2250)", "Wim Taymans "); } @@ -198,5 +198,5 @@ gboolean gst_rtp_mpv_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpmpvdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_MPV_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_MPV_DEPAY); } diff --git a/gst/rtp/gstrtpmpvpay.c b/gst/rtp/gstrtpmpvpay.c index 6523dbbedb..a96afbf0f1 100644 --- a/gst/rtp/gstrtpmpvpay.c +++ b/gst/rtp/gstrtpmpvpay.c @@ -48,12 +48,17 @@ GST_STATIC_PAD_TEMPLATE ("src", "clock-rate = (int) 90000, " "encoding-name = (string) \"MPV\"") ); +static GstStateChangeReturn gst_rtp_mpv_pay_change_state (GstElement * element, + GstStateChange transition); + +static void gst_rtp_mpv_pay_finalize (GObject * object); + +static GstFlowReturn gst_rtp_mpv_pay_flush (GstRTPMPVPay * rtpmpvpay); static gboolean gst_rtp_mpv_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps); static GstFlowReturn gst_rtp_mpv_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer); -static GstFlowReturn gst_rtp_mpv_pay_flush (GstRTPMPVPay * rtpmpvpay); -static void gst_rtp_mpv_pay_finalize (GObject * object); +static gboolean gst_rtp_mpv_pay_handle_event (GstPad * pad, GstEvent * event); GST_BOILERPLATE (GstRTPMPVPay, gst_rtp_mpv_pay, GstBaseRTPPayload, GST_TYPE_BASE_RTP_PAYLOAD); @@ -68,7 +73,7 @@ gst_rtp_mpv_pay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_mpv_pay_src_template)); gst_element_class_set_details_simple (element_class, - "RTP MPEG2 ES video payloader", "Codec/Payloader/Network", + "RTP MPEG2 ES video payloader", "Codec/Payloader/Network/RTP", "Payload-encodes MPEG2 ES into RTP packets (RFC 2250)", "Thijs Vermeir "); } @@ -77,15 +82,20 @@ static void gst_rtp_mpv_pay_class_init (GstRTPMPVPayClass * klass) { GObjectClass *gobject_class; + GstElementClass *gstelement_class; GstBaseRTPPayloadClass *gstbasertppayload_class; gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass; gobject_class->finalize = gst_rtp_mpv_pay_finalize; + gstelement_class->change_state = gst_rtp_mpv_pay_change_state; + gstbasertppayload_class->set_caps = gst_rtp_mpv_pay_setcaps; gstbasertppayload_class->handle_buffer = gst_rtp_mpv_pay_handle_buffer; + gstbasertppayload_class->handle_event = gst_rtp_mpv_pay_handle_event; GST_DEBUG_CATEGORY_INIT (rtpmpvpay_debug, "rtpmpvpay", 0, "MPEG2 ES Video RTP Payloader"); @@ -113,6 +123,15 @@ gst_rtp_mpv_pay_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } +static void +gst_rtp_mpv_pay_reset (GstRTPMPVPay * pay) +{ + pay->first_ts = -1; + pay->duration = 0; + gst_adapter_clear (pay->adapter); + GST_DEBUG_OBJECT (pay, "reset depayloader"); +} + static gboolean gst_rtp_mpv_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) { @@ -120,6 +139,31 @@ gst_rtp_mpv_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) return gst_basertppayload_set_outcaps (payload, NULL); } +static gboolean +gst_rtp_mpv_pay_handle_event (GstPad * pad, GstEvent * event) +{ + GstRTPMPVPay *rtpmpvpay; + + rtpmpvpay = GST_RTP_MPV_PAY (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* make sure we push the last packets in the adapter on EOS */ + gst_rtp_mpv_pay_flush (rtpmpvpay); + break; + case GST_EVENT_FLUSH_STOP: + gst_rtp_mpv_pay_reset (rtpmpvpay); + break; + default: + break; + } + + gst_object_unref (rtpmpvpay); + + /* FALSE to let the parent handle the event as well */ + return FALSE; +} + static GstFlowReturn gst_rtp_mpv_pay_flush (GstRTPMPVPay * rtpmpvpay) { @@ -191,6 +235,11 @@ gst_rtp_mpv_pay_handle_buffer (GstBaseRTPPayload * basepayload, timestamp = GST_BUFFER_TIMESTAMP (buffer); duration = GST_BUFFER_DURATION (buffer); + if (GST_BUFFER_IS_DISCONT (buffer)) { + GST_DEBUG_OBJECT (rtpmpvpay, "DISCONT"); + gst_rtp_mpv_pay_reset (rtpmpvpay); + } + avail = gst_adapter_available (rtpmpvpay->adapter); if (duration == -1) @@ -224,9 +273,38 @@ gst_rtp_mpv_pay_handle_buffer (GstBaseRTPPayload * basepayload, return ret; } +static GstStateChangeReturn +gst_rtp_mpv_pay_change_state (GstElement * element, GstStateChange transition) +{ + GstRTPMPVPay *rtpmpvpay; + GstStateChangeReturn ret; + + rtpmpvpay = GST_RTP_MPV_PAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_mpv_pay_reset (rtpmpvpay); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_mpv_pay_reset (rtpmpvpay); + break; + default: + break; + } + return ret; +} + + gboolean gst_rtp_mpv_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpmpvpay", - GST_RANK_NONE, GST_TYPE_RTP_MPV_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_MPV_PAY); } diff --git a/gst/rtp/gstrtppcmadepay.c b/gst/rtp/gstrtppcmadepay.c index acb5336050..35fc0a9891 100644 --- a/gst/rtp/gstrtppcmadepay.c +++ b/gst/rtp/gstrtppcmadepay.c @@ -78,7 +78,7 @@ gst_rtp_pcma_depay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_pcma_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP PCMA depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts PCMA audio from RTP packets", "Edgard Lima , Zeeshan Ali "); } @@ -158,5 +158,5 @@ gboolean gst_rtp_pcma_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtppcmadepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_PCMA_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_PCMA_DEPAY); } diff --git a/gst/rtp/gstrtppcmapay.c b/gst/rtp/gstrtppcmapay.c index 270f3663e3..d897ce1289 100644 --- a/gst/rtp/gstrtppcmapay.c +++ b/gst/rtp/gstrtppcmapay.c @@ -53,7 +53,7 @@ static GstStaticPadTemplate gst_rtp_pcma_pay_src_template = static gboolean gst_rtp_pcma_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps); -GST_BOILERPLATE (GstRtpPmcaPay, gst_rtp_pcma_pay, GstBaseRTPAudioPayload, +GST_BOILERPLATE (GstRtpPcmaPay, gst_rtp_pcma_pay, GstBaseRTPAudioPayload, GST_TYPE_BASE_RTP_AUDIO_PAYLOAD); static void @@ -66,13 +66,13 @@ gst_rtp_pcma_pay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_pcma_pay_src_template)); gst_element_class_set_details_simple (element_class, "RTP PCMA payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payload-encodes PCMA audio into a RTP packet", "Edgard Lima "); } static void -gst_rtp_pcma_pay_class_init (GstRtpPmcaPayClass * klass) +gst_rtp_pcma_pay_class_init (GstRtpPcmaPayClass * klass) { GstBaseRTPPayloadClass *gstbasertppayload_class; @@ -82,7 +82,7 @@ gst_rtp_pcma_pay_class_init (GstRtpPmcaPayClass * klass) } static void -gst_rtp_pcma_pay_init (GstRtpPmcaPay * rtppcmapay, GstRtpPmcaPayClass * klass) +gst_rtp_pcma_pay_init (GstRtpPcmaPay * rtppcmapay, GstRtpPcmaPayClass * klass) { GstBaseRTPAudioPayload *basertpaudiopayload; @@ -114,5 +114,5 @@ gboolean gst_rtp_pcma_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtppcmapay", - GST_RANK_NONE, GST_TYPE_RTP_PCMA_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_PCMA_PAY); } diff --git a/gst/rtp/gstrtppcmapay.h b/gst/rtp/gstrtppcmapay.h index 7a39d7dd7f..a51e975e4b 100644 --- a/gst/rtp/gstrtppcmapay.h +++ b/gst/rtp/gstrtppcmapay.h @@ -21,26 +21,26 @@ G_BEGIN_DECLS -typedef struct _GstRtpPmcaPay GstRtpPmcaPay; -typedef struct _GstRtpPmcaPayClass GstRtpPmcaPayClass; +typedef struct _GstRtpPcmaPay GstRtpPcmaPay; +typedef struct _GstRtpPcmaPayClass GstRtpPcmaPayClass; #define GST_TYPE_RTP_PCMA_PAY \ (gst_rtp_pcma_pay_get_type()) #define GST_RTP_PCMA_PAY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_PCMA_PAY,GstRtpPmcaPay)) + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_PCMA_PAY,GstRtpPcmaPay)) #define GST_RTP_PCMA_PAY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_PCMA_PAY,GstRtpPmcaPayClass)) + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_PCMA_PAY,GstRtpPcmaPayClass)) #define GST_IS_RTP_PCMA_PAY(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_PCMA_PAY)) #define GST_IS_RTP_PCMA_PAY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_PCMA_PAY)) -struct _GstRtpPmcaPay +struct _GstRtpPcmaPay { GstBaseRTPAudioPayload audiopayload; }; -struct _GstRtpPmcaPayClass +struct _GstRtpPcmaPayClass { GstBaseRTPAudioPayloadClass parent_class; }; diff --git a/gst/rtp/gstrtppcmudepay.c b/gst/rtp/gstrtppcmudepay.c index 9c47891f80..fd1f1e5021 100644 --- a/gst/rtp/gstrtppcmudepay.c +++ b/gst/rtp/gstrtppcmudepay.c @@ -78,7 +78,7 @@ gst_rtp_pcmu_depay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_pcmu_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP PCMU depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts PCMU audio from RTP packets", "Edgard Lima , Zeeshan Ali "); } @@ -158,5 +158,5 @@ gboolean gst_rtp_pcmu_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtppcmudepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_PCMU_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_PCMU_DEPAY); } diff --git a/gst/rtp/gstrtppcmupay.c b/gst/rtp/gstrtppcmupay.c index 19074ad86f..e7a0995539 100644 --- a/gst/rtp/gstrtppcmupay.c +++ b/gst/rtp/gstrtppcmupay.c @@ -66,7 +66,7 @@ gst_rtp_pcmu_pay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_pcmu_pay_src_template)); gst_element_class_set_details_simple (element_class, "RTP PCMU payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payload-encodes PCMU audio into a RTP packet", "Edgard Lima "); } @@ -114,5 +114,5 @@ gboolean gst_rtp_pcmu_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtppcmupay", - GST_RANK_NONE, GST_TYPE_RTP_PCMU_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_PCMU_PAY); } diff --git a/gst/rtp/gstrtpqcelpdepay.c b/gst/rtp/gstrtpqcelpdepay.c index 5499e307c4..badeb9f244 100644 --- a/gst/rtp/gstrtpqcelpdepay.c +++ b/gst/rtp/gstrtpqcelpdepay.c @@ -91,7 +91,7 @@ gst_rtp_qcelp_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_qcelp_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP QCELP depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts QCELP (PureVoice) audio from RTP packets (RFC 2658)", "Wim Taymans "); } @@ -431,5 +431,5 @@ gboolean gst_rtp_qcelp_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpqcelpdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_QCELP_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_QCELP_DEPAY); } diff --git a/gst/rtp/gstrtpqdmdepay.c b/gst/rtp/gstrtpqdmdepay.c index 1ab4d598f9..7e2e50b071 100644 --- a/gst/rtp/gstrtpqdmdepay.c +++ b/gst/rtp/gstrtpqdmdepay.c @@ -77,7 +77,7 @@ gst_rtp_qdm2_depay_base_init (gpointer klass) gst_element_class_set_details_simple (element_class, "RTP QDM2 depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts QDM2 audio from RTP packets (no RFC)", "Edward Hervey "); } @@ -415,5 +415,5 @@ gst_rtp_qdm2_depay_plugin_init (GstPlugin * plugin) "RTP QDM2 depayloader"); return gst_element_register (plugin, "rtpqdm2depay", - GST_RANK_MARGINAL, GST_TYPE_RTP_QDM2_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_QDM2_DEPAY); } diff --git a/gst/rtp/gstrtpsirendepay.c b/gst/rtp/gstrtpsirendepay.c index 736160fdf1..2746e74f82 100644 --- a/gst/rtp/gstrtpsirendepay.c +++ b/gst/rtp/gstrtpsirendepay.c @@ -64,7 +64,7 @@ gst_rtp_siren_depay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_siren_depay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP Siren packet depayloader", "Codec/Depayloader/Network", + "RTP Siren packet depayloader", "Codec/Depayloader/Network/RTP", "Extracts Siren audio from RTP packets", "Philippe Kalaf "); } @@ -120,5 +120,5 @@ gboolean gst_rtp_siren_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpsirendepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_SIREN_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_SIREN_DEPAY); } diff --git a/gst/rtp/gstrtpsirenpay.c b/gst/rtp/gstrtpsirenpay.c index dde01f4d6c..c7c9582af0 100644 --- a/gst/rtp/gstrtpsirenpay.c +++ b/gst/rtp/gstrtpsirenpay.c @@ -64,7 +64,7 @@ gst_rtp_siren_pay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_siren_pay_src_template)); gst_element_class_set_details_simple (element_class, - "RTP Payloader for Siren Audio", "Codec/Payloader/Network", + "RTP Payloader for Siren Audio", "Codec/Payloader/Network/RTP", "Packetize Siren audio streams into RTP packets", "Youness Alaoui "); } @@ -148,5 +148,5 @@ gboolean gst_rtp_siren_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpsirenpay", - GST_RANK_NONE, GST_TYPE_RTP_SIREN_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_SIREN_PAY); } diff --git a/gst/rtp/gstrtpspeexdepay.c b/gst/rtp/gstrtpspeexdepay.c index fe6d0045d7..10dea1a703 100644 --- a/gst/rtp/gstrtpspeexdepay.c +++ b/gst/rtp/gstrtpspeexdepay.c @@ -76,7 +76,7 @@ gst_rtp_speex_depay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_speex_depay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP Speex depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts Speex audio from RTP packets", "Edgard Lima "); } @@ -221,5 +221,5 @@ gboolean gst_rtp_speex_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpspeexdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_SPEEX_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_SPEEX_DEPAY); } diff --git a/gst/rtp/gstrtpspeexpay.c b/gst/rtp/gstrtpspeexpay.c index 7dfc6a96d7..a551fa9439 100644 --- a/gst/rtp/gstrtpspeexpay.c +++ b/gst/rtp/gstrtpspeexpay.c @@ -73,7 +73,7 @@ gst_rtp_speex_pay_base_init (gpointer klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_speex_pay_src_template)); gst_element_class_set_details_simple (element_class, "RTP Speex payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payload-encodes Speex audio into a RTP packet", "Edgard Lima "); @@ -332,5 +332,5 @@ gboolean gst_rtp_speex_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpspeexpay", - GST_RANK_NONE, GST_TYPE_RTP_SPEEX_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_SPEEX_PAY); } diff --git a/gst/rtp/gstrtpsv3vdepay.c b/gst/rtp/gstrtpsv3vdepay.c index 248ed45146..a99559cfe1 100644 --- a/gst/rtp/gstrtpsv3vdepay.c +++ b/gst/rtp/gstrtpsv3vdepay.c @@ -72,7 +72,7 @@ gst_rtp_sv3v_depay_base_init (gpointer klass) gst_element_class_set_details_simple (element_class, "RTP SVQ3 depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts SVQ3 video from RTP packets (no RFC)", "Wim Taymans "); } @@ -320,5 +320,5 @@ gst_rtp_sv3v_depay_plugin_init (GstPlugin * plugin) "RTP SV3V depayloader"); return gst_element_register (plugin, "rtpsv3vdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_SV3V_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_SV3V_DEPAY); } diff --git a/gst/rtp/gstrtptheoradepay.c b/gst/rtp/gstrtptheoradepay.c index 27ba21ce2e..c1a576bc5e 100644 --- a/gst/rtp/gstrtptheoradepay.c +++ b/gst/rtp/gstrtptheoradepay.c @@ -67,6 +67,8 @@ static gboolean gst_rtp_theora_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps); static GstBuffer *gst_rtp_theora_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf); +static gboolean gst_rtp_theora_depay_packet_lost (GstBaseRTPDepayload * + depayload, GstEvent * event); static void gst_rtp_theora_depay_finalize (GObject * object); @@ -82,7 +84,7 @@ gst_rtp_theora_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_theora_depay_src_template)); gst_element_class_set_details_simple (element_class, "RTP Theora depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts Theora video from RTP packets (draft-01 of RFC XXXX)", "Wim Taymans "); } @@ -100,6 +102,7 @@ gst_rtp_theora_depay_class_init (GstRtpTheoraDepayClass * klass) gstbasertpdepayload_class->process = gst_rtp_theora_depay_process; gstbasertpdepayload_class->set_caps = gst_rtp_theora_depay_setcaps; + gstbasertpdepayload_class->packet_lost = gst_rtp_theora_depay_packet_lost; GST_DEBUG_CATEGORY_INIT (rtptheoradepay_debug, "rtptheoradepay", 0, "Theora RTP Depayloader"); @@ -308,6 +311,8 @@ gst_rtp_theora_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload); + rtptheoradepay->needs_keyframe = FALSE; + structure = gst_caps_get_structure (caps, 0); /* read and parse configuration string */ @@ -496,7 +501,7 @@ gst_rtp_theora_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) to_free = payload; } - GST_DEBUG_OBJECT (depayload, "assemble done"); + GST_DEBUG_OBJECT (depayload, "assemble done, payload_len %d", payload_len); /* we not assembling anymore now */ rtptheoradepay->assembling = FALSE; @@ -519,7 +524,7 @@ gst_rtp_theora_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) */ timestamp = gst_rtp_buffer_get_timestamp (buf); - while (payload_len > 2) { + while (payload_len >= 2) { guint16 length; length = GST_READ_UINT16_BE (payload); @@ -554,6 +559,9 @@ gst_rtp_theora_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) memcpy (GST_BUFFER_DATA (outbuf), payload, length); } + if (payload_len > 0 && (payload[0] & 0xC0) == 0x0) + rtptheoradepay->needs_keyframe = FALSE; + payload += length; payload_len -= length; @@ -573,6 +581,9 @@ gst_rtp_theora_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) g_free (to_free); + if (rtptheoradepay->needs_keyframe) + goto request_keyframe; + return NULL; no_output: @@ -584,13 +595,13 @@ switch_failed: { GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE, (NULL), ("Could not switch codebooks")); - return NULL; + goto request_config; } packet_short: { GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE, (NULL), ("Packet was too short (%d < 4)", payload_len)); - return NULL; + goto request_keyframe; } ignore_reserved: { @@ -601,13 +612,29 @@ length_short: { GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE, (NULL), ("Packet contains invalid data")); - return NULL; + goto request_keyframe; } invalid_configuration: { /* fatal, as we otherwise risk carrying on without output */ GST_ELEMENT_ERROR (rtptheoradepay, STREAM, DECODE, (NULL), ("Packet contains invalid configuration")); + goto request_config; + } +request_config: + { + gst_pad_push_event (GST_BASE_RTP_DEPAYLOAD_SINKPAD (depayload), + gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, + gst_structure_new ("GstForceKeyUnit", + "all-headers", G_TYPE_BOOLEAN, TRUE, NULL))); + return NULL; + } +request_keyframe: + { + rtptheoradepay->needs_keyframe = TRUE; + gst_pad_push_event (GST_BASE_RTP_DEPAYLOAD_SINKPAD (depayload), + gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, + gst_structure_new ("GstForceKeyUnit", NULL))); return NULL; } } @@ -616,5 +643,24 @@ gboolean gst_rtp_theora_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtptheoradepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_THEORA_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_THEORA_DEPAY); +} + +static gboolean +gst_rtp_theora_depay_packet_lost (GstBaseRTPDepayload * depayload, + GstEvent * event) +{ + GstRtpTheoraDepay *rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload); + guint seqnum = 0; + + gst_structure_get_uint (event->structure, "seqnum", &seqnum); + GST_LOG_OBJECT (depayload, "Requested keyframe because frame with seqnum %u" + " is missing", seqnum); + rtptheoradepay->needs_keyframe = TRUE; + + gst_pad_push_event (GST_BASE_RTP_DEPAYLOAD_SINKPAD (depayload), + gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, + gst_structure_new ("GstForceKeyUnit", NULL))); + + return TRUE; } diff --git a/gst/rtp/gstrtptheoradepay.h b/gst/rtp/gstrtptheoradepay.h index 0d1a911f9f..dff261c43c 100644 --- a/gst/rtp/gstrtptheoradepay.h +++ b/gst/rtp/gstrtptheoradepay.h @@ -54,6 +54,8 @@ struct _GstRtpTheoraDepay GstAdapter *adapter; gboolean assembling; + + gboolean needs_keyframe; }; struct _GstRtpTheoraDepayClass diff --git a/gst/rtp/gstrtptheorapay.c b/gst/rtp/gstrtptheorapay.c index 82651eca86..0092fbc548 100644 --- a/gst/rtp/gstrtptheorapay.c +++ b/gst/rtp/gstrtptheorapay.c @@ -84,6 +84,9 @@ static GstStateChangeReturn gst_rtp_theora_pay_change_state (GstElement * element, GstStateChange transition); static GstFlowReturn gst_rtp_theora_pay_handle_buffer (GstBaseRTPPayload * pad, GstBuffer * buffer); +static gboolean gst_rtp_theora_pay_handle_event (GstPad * pad, + GstEvent * event); + static void gst_rtp_theora_pay_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); @@ -101,7 +104,7 @@ gst_rtp_theora_pay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_theora_pay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP Theora payloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payload-encode Theora video into RTP packets (draft-01 RFC XXXX)", "Wim Taymans "); } @@ -121,6 +124,7 @@ gst_rtp_theora_pay_class_init (GstRtpTheoraPayClass * klass) gstbasertppayload_class->set_caps = gst_rtp_theora_pay_setcaps; gstbasertppayload_class->handle_buffer = gst_rtp_theora_pay_handle_buffer; + gstbasertppayload_class->handle_event = gst_rtp_theora_pay_handle_event; gobject_class->set_property = gst_rtp_theora_pay_set_property; gobject_class->get_property = gst_rtp_theora_pay_get_property; @@ -144,6 +148,14 @@ gst_rtp_theora_pay_init (GstRtpTheoraPay * rtptheorapay, rtptheorapay->last_config = GST_CLOCK_TIME_NONE; } +static void +gst_rtp_theora_pay_clear_packet (GstRtpTheoraPay * rtptheorapay) +{ + if (rtptheorapay->packet) + gst_buffer_unref (rtptheorapay->packet); + rtptheorapay->packet = NULL; +} + static void gst_rtp_theora_pay_cleanup (GstRtpTheoraPay * rtptheorapay) { @@ -151,9 +163,7 @@ gst_rtp_theora_pay_cleanup (GstRtpTheoraPay * rtptheorapay) g_list_free (rtptheorapay->headers); rtptheorapay->headers = NULL; - if (rtptheorapay->packet) - gst_buffer_unref (rtptheorapay->packet); - rtptheorapay->packet = NULL; + gst_rtp_theora_pay_clear_packet (rtptheorapay); if (rtptheorapay->config_data) g_free (rtptheorapay->config_data); @@ -553,7 +563,7 @@ gst_rtp_theora_pay_payload_buffer (GstRtpTheoraPay * rtptheorapay, guint8 TDT, /* put buffer in packet, it either fits completely or needs to be fragmented * over multiple RTP packets. */ - while (size) { + do { plen = MIN (rtptheorapay->payload_left - 2, size); GST_DEBUG_OBJECT (rtptheorapay, "append %u bytes", plen); @@ -561,7 +571,8 @@ gst_rtp_theora_pay_payload_buffer (GstRtpTheoraPay * rtptheorapay, guint8 TDT, /* data is copied in the payload with a 2 byte length header */ ppos[0] = ((plen - not_in_length) >> 8) & 0xff; ppos[1] = ((plen - not_in_length) & 0xff); - memcpy (&ppos[2], data, plen); + if (plen) + memcpy (&ppos[2], data, plen); /* only first (only) configuration cuts length field */ /* NOTE: spec (if any) is not clear on this ... */ @@ -607,7 +618,7 @@ gst_rtp_theora_pay_payload_buffer (GstRtpTheoraPay * rtptheorapay, guint8 TDT, if (duration != GST_CLOCK_TIME_NONE) rtptheorapay->payload_duration += duration; } - } + } while (size); return ret; } @@ -634,11 +645,14 @@ gst_rtp_theora_pay_handle_buffer (GstBaseRTPPayload * basepayload, GST_DEBUG_OBJECT (rtptheorapay, "size %u, duration %" GST_TIME_FORMAT, size, GST_TIME_ARGS (duration)); - if (G_UNLIKELY (size < 1 || size > 0xffff)) + if (G_UNLIKELY (size > 0xffff)) goto wrong_size; /* find packet type */ - if (data[0] & 0x80) { + if (size == 0) { + TDT = 0; + keyframe = FALSE; + } else if (data[0] & 0x80) { /* header */ if (data[0] == 0x80) { /* identification, we need to parse this in order to get the clock rate. @@ -733,7 +747,7 @@ done: wrong_size: { GST_ELEMENT_WARNING (rtptheorapay, STREAM, DECODE, - ("Invalid packet size (1 < %d <= 0xffff)", size), (NULL)); + ("Invalid packet size (%d <= 0xffff)", size), (NULL)); gst_buffer_unref (buffer); return GST_FLOW_OK; } @@ -758,6 +772,22 @@ header_error: } } +static gboolean +gst_rtp_theora_pay_handle_event (GstPad * pad, GstEvent * event) +{ + GstRtpTheoraPay *rtptheorapay = GST_RTP_THEORA_PAY (GST_PAD_PARENT (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_rtp_theora_pay_clear_packet (rtptheorapay); + break; + default: + break; + } + /* false to let parent handle event as well */ + return FALSE; +} + static GstStateChangeReturn gst_rtp_theora_pay_change_state (GstElement * element, GstStateChange transition) @@ -823,5 +853,5 @@ gboolean gst_rtp_theora_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtptheorapay", - GST_RANK_NONE, GST_TYPE_RTP_THEORA_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_THEORA_PAY); } diff --git a/gst/rtp/gstrtpvorbisdepay.c b/gst/rtp/gstrtpvorbisdepay.c index 22f62b8f68..506558defe 100644 --- a/gst/rtp/gstrtpvorbisdepay.c +++ b/gst/rtp/gstrtpvorbisdepay.c @@ -82,7 +82,7 @@ gst_rtp_vorbis_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_vorbis_depay_src_template)); gst_element_class_set_details_simple (element_class, "RTP Vorbis depayloader", - "Codec/Depayloader/Network", + "Codec/Depayloader/Network/RTP", "Extracts Vorbis Audio from RTP packets (RFC 5215)", "Wim Taymans "); } @@ -696,5 +696,5 @@ gboolean gst_rtp_vorbis_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpvorbisdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_VORBIS_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_VORBIS_DEPAY); } diff --git a/gst/rtp/gstrtpvorbispay.c b/gst/rtp/gstrtpvorbispay.c index ae16253520..a7841c382a 100644 --- a/gst/rtp/gstrtpvorbispay.c +++ b/gst/rtp/gstrtpvorbispay.c @@ -67,6 +67,8 @@ static GstStateChangeReturn gst_rtp_vorbis_pay_change_state (GstElement * element, GstStateChange transition); static GstFlowReturn gst_rtp_vorbis_pay_handle_buffer (GstBaseRTPPayload * pad, GstBuffer * buffer); +static gboolean gst_rtp_vorbis_pay_handle_event (GstPad * pad, + GstEvent * event); static void gst_rtp_vorbis_pay_base_init (gpointer klass) @@ -79,7 +81,7 @@ gst_rtp_vorbis_pay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_vorbis_pay_sink_template)); gst_element_class_set_details_simple (element_class, "RTP Vorbis depayloader", - "Codec/Payloader/Network", + "Codec/Payloader/Network/RTP", "Payload-encode Vorbis audio into RTP packets (RFC 5215)", "Wim Taymans "); } @@ -97,6 +99,7 @@ gst_rtp_vorbis_pay_class_init (GstRtpVorbisPayClass * klass) gstbasertppayload_class->set_caps = gst_rtp_vorbis_pay_setcaps; gstbasertppayload_class->handle_buffer = gst_rtp_vorbis_pay_handle_buffer; + gstbasertppayload_class->handle_event = gst_rtp_vorbis_pay_handle_event; GST_DEBUG_CATEGORY_INIT (rtpvorbispay_debug, "rtpvorbispay", 0, "Vorbis RTP Payloader"); @@ -109,6 +112,14 @@ gst_rtp_vorbis_pay_init (GstRtpVorbisPay * rtpvorbispay, /* needed because of GST_BOILERPLATE */ } +static void +gst_rtp_vorbis_pay_clear_packet (GstRtpVorbisPay * rtpvorbispay) +{ + if (rtpvorbispay->packet) + gst_buffer_unref (rtpvorbispay->packet); + rtpvorbispay->packet = NULL; +} + static void gst_rtp_vorbis_pay_cleanup (GstRtpVorbisPay * rtpvorbispay) { @@ -116,9 +127,7 @@ gst_rtp_vorbis_pay_cleanup (GstRtpVorbisPay * rtpvorbispay) g_list_free (rtpvorbispay->headers); rtpvorbispay->headers = NULL; - if (rtpvorbispay->packet) - gst_buffer_unref (rtpvorbispay->packet); - rtpvorbispay->packet = NULL; + gst_rtp_vorbis_pay_clear_packet (rtpvorbispay); } static gboolean @@ -635,6 +644,22 @@ header_error: } } +static gboolean +gst_rtp_vorbis_pay_handle_event (GstPad * pad, GstEvent * event) +{ + GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (GST_PAD_PARENT (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_rtp_vorbis_pay_clear_packet (rtpvorbispay); + break; + default: + break; + } + /* false to let parent handle event as well */ + return FALSE; +} + static GstStateChangeReturn gst_rtp_vorbis_pay_change_state (GstElement * element, GstStateChange transition) @@ -665,5 +690,5 @@ gboolean gst_rtp_vorbis_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpvorbispay", - GST_RANK_NONE, GST_TYPE_RTP_VORBIS_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_VORBIS_PAY); } diff --git a/gst/rtp/gstrtpvrawdepay.c b/gst/rtp/gstrtpvrawdepay.c index 54a4b52875..5919a5bcb0 100644 --- a/gst/rtp/gstrtpvrawdepay.c +++ b/gst/rtp/gstrtpvrawdepay.c @@ -58,6 +58,9 @@ static GstBuffer *gst_rtp_vraw_depay_process (GstBaseRTPDepayload * depayload, static GstStateChangeReturn gst_rtp_vraw_depay_change_state (GstElement * element, GstStateChange transition); +static gboolean gst_rtp_vraw_depay_handle_event (GstBaseRTPDepayload * filter, + GstEvent * event); + static void gst_rtp_vraw_depay_base_init (gpointer klass) { @@ -69,7 +72,7 @@ gst_rtp_vraw_depay_base_init (gpointer klass) gst_static_pad_template_get (&gst_rtp_vraw_depay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP Raw Video depayloader", "Codec/Depayloader/Network", + "RTP Raw Video depayloader", "Codec/Depayloader/Network/RTP", "Extracts raw video from RTP packets (RFC 4175)", "Wim Taymans "); } @@ -87,6 +90,7 @@ gst_rtp_vraw_depay_class_init (GstRtpVRawDepayClass * klass) gstbasertpdepayload_class->set_caps = gst_rtp_vraw_depay_setcaps; gstbasertpdepayload_class->process = gst_rtp_vraw_depay_process; + gstbasertpdepayload_class->handle_event = gst_rtp_vraw_depay_handle_event; GST_DEBUG_CATEGORY_INIT (rtpvrawdepay_debug, "rtpvrawdepay", 0, "raw video RTP Depayloader"); @@ -99,6 +103,16 @@ gst_rtp_vraw_depay_init (GstRtpVRawDepay * rtpvrawdepay, /* needed because of GST_BOILERPLATE */ } +static void +gst_rtp_vraw_depay_reset (GstRtpVRawDepay * rtpvrawdepay) +{ + if (rtpvrawdepay->outbuf) { + gst_buffer_unref (rtpvrawdepay->outbuf); + rtpvrawdepay->outbuf = NULL; + } + rtpvrawdepay->timestamp = -1; +} + static gboolean gst_rtp_vraw_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) { @@ -228,6 +242,7 @@ gst_rtp_vraw_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) GST_DEBUG_OBJECT (depayload, "width %d, height %d, format %d", width, height, format); GST_DEBUG_OBJECT (depayload, "yp %d, up %d, vp %d", yp, up, vp); + GST_DEBUG_OBJECT (depayload, "xinc %d, yinc %d", xinc, yinc); GST_DEBUG_OBJECT (depayload, "pgroup %d, ystride %d, uvstride %d", pgroup, ystride, uvstride); GST_DEBUG_OBJECT (depayload, "outsize %u", outsize); @@ -372,10 +387,10 @@ gst_rtp_vraw_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) } /* calculate the maximim amount of bytes we can use per line */ - if (offs + ((length / pgroup) * xinc) > (width - xinc)) { + if (offs + ((length / pgroup) * xinc) > width) { plen = ((width - offs) * pgroup) / xinc; - GST_WARNING_OBJECT (depayload, "clipping length %d, offset %d", length, - offs); + GST_WARNING_OBJECT (depayload, "clipping length %d, offset %d, plen %d", + length, offs, plen); } else plen = length; @@ -513,6 +528,28 @@ short_packet: } } +static gboolean +gst_rtp_vraw_depay_handle_event (GstBaseRTPDepayload * filter, GstEvent * event) +{ + gboolean ret; + GstRtpVRawDepay *rtpvrawdepay; + + rtpvrawdepay = GST_RTP_VRAW_DEPAY (filter); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_rtp_vraw_depay_reset (rtpvrawdepay); + break; + default: + break; + } + + ret = + GST_BASE_RTP_DEPAYLOAD_CLASS (parent_class)->handle_event (filter, event); + + return ret; +} + static GstStateChangeReturn gst_rtp_vraw_depay_change_state (GstElement * element, GstStateChange transition) @@ -526,7 +563,7 @@ gst_rtp_vraw_depay_change_state (GstElement * element, case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: - rtpvrawdepay->timestamp = -1; + gst_rtp_vraw_depay_reset (rtpvrawdepay); break; default: break; @@ -536,10 +573,7 @@ gst_rtp_vraw_depay_change_state (GstElement * element, switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: - if (rtpvrawdepay->outbuf) { - gst_buffer_unref (rtpvrawdepay->outbuf); - rtpvrawdepay->outbuf = NULL; - } + gst_rtp_vraw_depay_reset (rtpvrawdepay); break; case GST_STATE_CHANGE_READY_TO_NULL: break; @@ -553,5 +587,5 @@ gboolean gst_rtp_vraw_depay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpvrawdepay", - GST_RANK_MARGINAL, GST_TYPE_RTP_VRAW_DEPAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_VRAW_DEPAY); } diff --git a/gst/rtp/gstrtpvrawpay.c b/gst/rtp/gstrtpvrawpay.c index aee077f763..c354639564 100644 --- a/gst/rtp/gstrtpvrawpay.c +++ b/gst/rtp/gstrtpvrawpay.c @@ -73,7 +73,7 @@ static GstStaticPadTemplate gst_rtp_vraw_pay_sink_template = "width = (int) [ 1, 32767 ], " "height = (int) [ 1, 32767 ]; " "video/x-raw-yuv, " - "format = (fourcc) { AYUV, UYVY, I420, Y41B }, " + "format = (fourcc) { AYUV, UYVY, I420, Y41B, UYVP }, " "width = (int) [ 1, 32767 ], " "height = (int) [ 1, 32767 ]; ") ); @@ -122,7 +122,7 @@ GST_BOILERPLATE (GstRtpVRawPay, gst_rtp_vraw_pay, GstBaseRTPPayload, gst_static_pad_template_get (&gst_rtp_vraw_pay_sink_template)); gst_element_class_set_details_simple (element_class, - "RTP Raw Video payloader", "Codec/Payloader/Network", + "RTP Raw Video payloader", "Codec/Payloader/Network/RTP", "Payload raw video as RTP packets (RFC 4175)", "Wim Taymans "); } @@ -160,6 +160,8 @@ gst_rtp_vraw_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) const gchar *depthstr, *samplingstr, *colorimetrystr; gchar *wstr, *hstr; gboolean interlaced; + const gchar *color_matrix; + gint depth; rtpvrawpay = GST_RTP_VRAW_PAY (payload); @@ -170,7 +172,6 @@ gst_rtp_vraw_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) /* these values are the only thing we can do */ depthstr = "8"; - colorimetrystr = "SMPTE240M"; /* parse common width/height */ res = gst_structure_get_int (s, "width", &width); @@ -178,12 +179,19 @@ 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; + color_matrix = gst_structure_get_string (s, "color-matrix"); + colorimetrystr = "SMPTE240M"; + if (color_matrix) { + if (g_str_equal (color_matrix, "sdtv")) { + /* BT.601 implies a bit more than just color-matrix */ + colorimetrystr = "BT601-5"; + } else if (g_str_equal (color_matrix, "hdtv")) { + colorimetrystr = "BT709-2"; + } + } yp = up = vp = 0; xinc = yinc = 1; @@ -193,6 +201,7 @@ gst_rtp_vraw_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) gboolean has_alpha; has_alpha = gst_structure_get_int (s, "alpha_mask", &amask); + depth = 8; if (!gst_structure_get_int (s, "red_mask", &rmask)) goto unknown_mask; @@ -233,6 +242,7 @@ gst_rtp_vraw_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) samplingstr = "YCbCr-4:4:4"; pgroup = 3; ystride = width * 4; + depth = 8; break; case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'): sampling = GST_VIDEO_FORMAT_UYVY; @@ -240,6 +250,7 @@ gst_rtp_vraw_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) pgroup = 4; xinc = 2; ystride = GST_ROUND_UP_2 (width) * 2; + depth = 8; break; case GST_MAKE_FOURCC ('Y', '4', '1', 'B'): sampling = GST_VIDEO_FORMAT_Y41B; @@ -250,6 +261,7 @@ gst_rtp_vraw_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) uvstride = GST_ROUND_UP_8 (width) / 4; up = ystride * height; vp = up + uvstride * height; + depth = 8; break; case GST_MAKE_FOURCC ('I', '4', '2', '0'): sampling = GST_VIDEO_FORMAT_I420; @@ -260,6 +272,16 @@ gst_rtp_vraw_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) uvstride = GST_ROUND_UP_8 (width) / 2; up = ystride * GST_ROUND_UP_2 (height); vp = up + uvstride * GST_ROUND_UP_2 (height) / 2; + depth = 8; + break; + case GST_MAKE_FOURCC ('U', 'Y', 'V', 'P'): +#define GST_VIDEO_FORMAT_UYVP GST_VIDEO_FORMAT_UYVY /* FIXME */ + sampling = GST_VIDEO_FORMAT_UYVP; + samplingstr = "YCbCr-4:2:2"; + pgroup = 4; + xinc = 2; + ystride = GST_ROUND_UP_2 (width) * 2; + depth = 10; break; default: goto unknown_fourcc; @@ -267,6 +289,13 @@ gst_rtp_vraw_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) } else goto unknown_format; + if (interlaced) { + yinc *= 2; + } + if (depth == 10) { + depthstr = "10"; + } + rtpvrawpay->width = width; rtpvrawpay->height = height; rtpvrawpay->sampling = sampling; @@ -278,6 +307,8 @@ gst_rtp_vraw_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) rtpvrawpay->vp = vp; rtpvrawpay->ystride = ystride; rtpvrawpay->uvstride = uvstride; + rtpvrawpay->interlaced = interlaced; + rtpvrawpay->depth = depth; GST_DEBUG_OBJECT (payload, "width %d, height %d, sampling %d", width, height, sampling); @@ -289,10 +320,17 @@ gst_rtp_vraw_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) hstr = g_strdup_printf ("%d", rtpvrawpay->height); gst_basertppayload_set_options (payload, "video", TRUE, "RAW", 90000); - res = gst_basertppayload_set_outcaps (payload, "sampling", G_TYPE_STRING, - samplingstr, "depth", G_TYPE_STRING, depthstr, "width", G_TYPE_STRING, - wstr, "height", G_TYPE_STRING, hstr, "colorimetry", G_TYPE_STRING, - colorimetrystr, NULL); + if (interlaced) { + res = gst_basertppayload_set_outcaps (payload, "sampling", G_TYPE_STRING, + samplingstr, "depth", G_TYPE_STRING, depthstr, "width", G_TYPE_STRING, + wstr, "height", G_TYPE_STRING, hstr, "colorimetry", G_TYPE_STRING, + colorimetrystr, "interlace", G_TYPE_STRING, "true", NULL); + } else { + res = gst_basertppayload_set_outcaps (payload, "sampling", G_TYPE_STRING, + samplingstr, "depth", G_TYPE_STRING, depthstr, "width", G_TYPE_STRING, + wstr, "height", G_TYPE_STRING, hstr, "colorimetry", G_TYPE_STRING, + colorimetrystr, NULL); + } g_free (wstr); g_free (hstr); @@ -314,11 +352,6 @@ 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"); @@ -337,6 +370,7 @@ gst_rtp_vraw_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer) guint size, pgroup; guint mtu; guint width, height; + gint field; rtpvrawpay = GST_RTP_VRAW_PAY (payload); @@ -361,220 +395,227 @@ gst_rtp_vraw_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer) height = rtpvrawpay->height; /* start with line 0, offset 0 */ - line = 0; - offset = 0; - /* write all lines */ - while (line < height) { - guint left; - GstBuffer *out; - guint8 *outdata, *headers; - gboolean next_line; - guint length, cont, pixels, fieldid; + for (field = 0; field < 1 + rtpvrawpay->interlaced; field++) { + line = field; + offset = 0; - /* get the max allowed payload length size, we try to fill the complete MTU */ - left = gst_rtp_buffer_calc_payload_len (mtu, 0, 0); - out = gst_rtp_buffer_new_allocate (left, 0, 0); + /* write all lines */ + while (line < height) { + guint left; + GstBuffer *out; + guint8 *outdata, *headers; + gboolean next_line; + guint length, cont, pixels; - GST_BUFFER_TIMESTAMP (out) = GST_BUFFER_TIMESTAMP (buffer); + /* get the max allowed payload length size, we try to fill the complete MTU */ + left = gst_rtp_buffer_calc_payload_len (mtu, 0, 0); + out = gst_rtp_buffer_new_allocate (left, 0, 0); - outdata = gst_rtp_buffer_get_payload (out); - - 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; - left -= 2; - - /* the headers start here */ - headers = outdata; - - /* while we can fit at least one header and one pixel */ - while (left > (6 + pgroup)) { - /* we need a 6 bytes header */ - left -= 6; - - /* get how may bytes we need for the remaining pixels */ - pixels = width - offset; - length = (pixels * pgroup) / rtpvrawpay->xinc; - - if (left >= length) { - /* pixels and header fit completely, we will write them and skip to the - * next line. */ - next_line = TRUE; + if (field == 0) { + GST_BUFFER_TIMESTAMP (out) = GST_BUFFER_TIMESTAMP (buffer); } else { - /* line does not fit completely, see how many pixels fit */ - pixels = (left / pgroup) * rtpvrawpay->xinc; + GST_BUFFER_TIMESTAMP (out) = GST_BUFFER_TIMESTAMP (buffer) + + GST_BUFFER_DURATION (buffer) / 2; + } + + outdata = gst_rtp_buffer_get_payload (out); + + 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; + left -= 2; + + /* the headers start here */ + headers = outdata; + + /* while we can fit at least one header and one pixel */ + while (left > (6 + pgroup)) { + /* we need a 6 bytes header */ + left -= 6; + + /* get how may bytes we need for the remaining pixels */ + pixels = width - offset; length = (pixels * pgroup) / rtpvrawpay->xinc; - next_line = FALSE; - } - GST_LOG_OBJECT (rtpvrawpay, "filling %u bytes in %u pixels", length, - pixels); - left -= length; - /* write length */ - *outdata++ = (length >> 8) & 0xff; - *outdata++ = length & 0xff; - - /* always 0 for now */ - fieldid = 0x00; - - /* write line no */ - *outdata++ = ((line >> 8) & 0x7f) | fieldid; - *outdata++ = line & 0xff; - - if (next_line) { - /* go to next line we do this here to make the check below easier */ - line += rtpvrawpay->yinc; - } - - /* calculate continuation marker */ - cont = (left > (6 + pgroup) && line < height) ? 0x80 : 0x00; - - /* write offset and continuation marker */ - *outdata++ = ((offset >> 8) & 0x7f) | cont; - *outdata++ = offset & 0xff; - - if (next_line) { - /* reset offset */ - offset = 0; - GST_LOG_OBJECT (rtpvrawpay, "go to next line %u", line); - } else { - offset += pixels; - GST_LOG_OBJECT (rtpvrawpay, "next offset %u", offset); - } - - if (!cont) - break; - } - GST_LOG_OBJECT (rtpvrawpay, "consumed %u bytes", - (guint) (outdata - headers)); - - /* second pass, read headers and write the data */ - while (TRUE) { - guint offs, lin; - - /* read length and cont */ - length = (headers[0] << 8) | headers[1]; - lin = ((headers[2] & 0x7f) << 8) | headers[3]; - offs = ((headers[4] & 0x7f) << 8) | headers[5]; - cont = headers[4] & 0x80; - pixels = length / pgroup; - headers += 6; - - GST_LOG_OBJECT (payload, "writing length %u, line %u, offset %u, cont %d", - length, lin, offs, cont); - - switch (rtpvrawpay->sampling) { - case GST_VIDEO_FORMAT_RGB: - case GST_VIDEO_FORMAT_RGBA: - case GST_VIDEO_FORMAT_BGR: - case GST_VIDEO_FORMAT_BGRA: - case GST_VIDEO_FORMAT_UYVY: - offs /= rtpvrawpay->xinc; - memcpy (outdata, yp + (lin * ystride) + (offs * pgroup), length); - outdata += length; - break; - case GST_VIDEO_FORMAT_AYUV: - { - gint i; - guint8 *datap; - - datap = yp + (lin * ystride) + (offs * 4); - - for (i = 0; i < pixels; i++) { - *outdata++ = datap[2]; - *outdata++ = datap[1]; - *outdata++ = datap[3]; - datap += 4; - } - break; + if (left >= length) { + /* pixels and header fit completely, we will write them and skip to the + * next line. */ + next_line = TRUE; + } else { + /* line does not fit completely, see how many pixels fit */ + pixels = (left / pgroup) * rtpvrawpay->xinc; + length = (pixels * pgroup) / rtpvrawpay->xinc; + next_line = FALSE; } - case GST_VIDEO_FORMAT_I420: - { - gint i; - guint uvoff; - guint8 *yd1p, *yd2p, *udp, *vdp; + GST_LOG_OBJECT (rtpvrawpay, "filling %u bytes in %u pixels", length, + pixels); + left -= length; - yd1p = yp + (lin * ystride) + (offs); - yd2p = yd1p + ystride; - uvoff = - (lin / rtpvrawpay->yinc * uvstride) + (offs / rtpvrawpay->xinc); - udp = up + uvoff; - vdp = vp + uvoff; + /* write length */ + *outdata++ = (length >> 8) & 0xff; + *outdata++ = length & 0xff; - for (i = 0; i < pixels; i++) { - *outdata++ = *yd1p++; - *outdata++ = *yd1p++; - *outdata++ = *yd2p++; - *outdata++ = *yd2p++; - *outdata++ = *udp++; - *outdata++ = *vdp++; - } - break; + /* write line no */ + *outdata++ = ((line >> 8) & 0x7f) | ((field << 7) & 0x80); + *outdata++ = line & 0xff; + + if (next_line) { + /* go to next line we do this here to make the check below easier */ + line += rtpvrawpay->yinc; } - case GST_VIDEO_FORMAT_Y41B: - { - gint i; - guint uvoff; - guint8 *ydp, *udp, *vdp; - ydp = yp + (lin * ystride) + offs; - uvoff = - (lin / rtpvrawpay->yinc * uvstride) + (offs / rtpvrawpay->xinc); - udp = up + uvoff; - vdp = vp + uvoff; + /* calculate continuation marker */ + cont = (left > (6 + pgroup) && line < height) ? 0x80 : 0x00; - for (i = 0; i < pixels; i++) { - *outdata++ = *udp++; - *outdata++ = *ydp++; - *outdata++ = *ydp++; - *outdata++ = *vdp++; - *outdata++ = *ydp++; - *outdata++ = *ydp++; - } - break; + /* write offset and continuation marker */ + *outdata++ = ((offset >> 8) & 0x7f) | cont; + *outdata++ = offset & 0xff; + + if (next_line) { + /* reset offset */ + offset = 0; + GST_LOG_OBJECT (rtpvrawpay, "go to next line %u", line); + } else { + offset += pixels; + GST_LOG_OBJECT (rtpvrawpay, "next offset %u", offset); } - default: - gst_buffer_unref (out); - goto unknown_sampling; + + if (!cont) + break; + } + GST_LOG_OBJECT (rtpvrawpay, "consumed %u bytes", + (guint) (outdata - headers)); + + /* second pass, read headers and write the data */ + while (TRUE) { + guint offs, lin; + + /* read length and cont */ + length = (headers[0] << 8) | headers[1]; + lin = ((headers[2] & 0x7f) << 8) | headers[3]; + offs = ((headers[4] & 0x7f) << 8) | headers[5]; + cont = headers[4] & 0x80; + pixels = length / pgroup; + headers += 6; + + GST_LOG_OBJECT (payload, + "writing length %u, line %u, offset %u, cont %d", length, lin, offs, + cont); + + switch (rtpvrawpay->sampling) { + case GST_VIDEO_FORMAT_RGB: + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_BGR: + case GST_VIDEO_FORMAT_BGRA: + case GST_VIDEO_FORMAT_UYVY: + offs /= rtpvrawpay->xinc; + memcpy (outdata, yp + (lin * ystride) + (offs * pgroup), length); + outdata += length; + break; + case GST_VIDEO_FORMAT_AYUV: + { + gint i; + guint8 *datap; + + datap = yp + (lin * ystride) + (offs * 4); + + for (i = 0; i < pixels; i++) { + *outdata++ = datap[2]; + *outdata++ = datap[1]; + *outdata++ = datap[3]; + datap += 4; + } + break; + } + case GST_VIDEO_FORMAT_I420: + { + gint i; + guint uvoff; + guint8 *yd1p, *yd2p, *udp, *vdp; + + yd1p = yp + (lin * ystride) + (offs); + yd2p = yd1p + ystride; + uvoff = + (lin / rtpvrawpay->yinc * uvstride) + (offs / rtpvrawpay->xinc); + udp = up + uvoff; + vdp = vp + uvoff; + + for (i = 0; i < pixels; i++) { + *outdata++ = *yd1p++; + *outdata++ = *yd1p++; + *outdata++ = *yd2p++; + *outdata++ = *yd2p++; + *outdata++ = *udp++; + *outdata++ = *vdp++; + } + break; + } + case GST_VIDEO_FORMAT_Y41B: + { + gint i; + guint uvoff; + guint8 *ydp, *udp, *vdp; + + ydp = yp + (lin * ystride) + offs; + uvoff = + (lin / rtpvrawpay->yinc * uvstride) + (offs / rtpvrawpay->xinc); + udp = up + uvoff; + vdp = vp + uvoff; + + for (i = 0; i < pixels; i++) { + *outdata++ = *udp++; + *outdata++ = *ydp++; + *outdata++ = *ydp++; + *outdata++ = *vdp++; + *outdata++ = *ydp++; + *outdata++ = *ydp++; + } + break; + } + default: + gst_buffer_unref (out); + goto unknown_sampling; + } + + if (!cont) + break; } - if (!cont) - break; + if (line >= height) { + GST_LOG_OBJECT (rtpvrawpay, "field/frame complete, set marker"); + gst_rtp_buffer_set_marker (out, TRUE); + } + if (left > 0) { + GST_LOG_OBJECT (rtpvrawpay, "we have %u bytes left", left); + GST_BUFFER_SIZE (out) -= left; + } + + /* push buffer */ + ret = gst_basertppayload_push (payload, out); } - if (line >= height) { - GST_LOG_OBJECT (rtpvrawpay, "frame complete, set marker"); - gst_rtp_buffer_set_marker (out, TRUE); - } - if (left > 0) { - GST_LOG_OBJECT (rtpvrawpay, "we have %u bytes left", left); - GST_BUFFER_SIZE (out) -= left; - } - - /* push buffer */ - ret = gst_basertppayload_push (payload, out); } gst_buffer_unref (buffer); @@ -594,5 +635,5 @@ gboolean gst_rtp_vraw_pay_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpvrawpay", - GST_RANK_NONE, GST_TYPE_RTP_VRAW_PAY); + GST_RANK_SECONDARY, GST_TYPE_RTP_VRAW_PAY); } diff --git a/gst/rtp/gstrtpvrawpay.h b/gst/rtp/gstrtpvrawpay.h index 44a7ee1b3d..0e6adf46dc 100644 --- a/gst/rtp/gstrtpvrawpay.h +++ b/gst/rtp/gstrtpvrawpay.h @@ -52,6 +52,8 @@ struct _GstRtpVRawPay guint yp, up, vp; gint ystride; gint uvstride; + gboolean interlaced; + gint depth; }; struct _GstRtpVRawPayClass diff --git a/gst/rtpmanager/gstrtpbin-marshal.list b/gst/rtpmanager/gstrtpbin-marshal.list index 01b530be41..7d9821f512 100644 --- a/gst/rtpmanager/gstrtpbin-marshal.list +++ b/gst/rtpmanager/gstrtpbin-marshal.list @@ -7,3 +7,6 @@ VOID:UINT VOID:UINT,UINT VOID:OBJECT,OBJECT UINT64:BOOL,UINT64 +BOOL:POINTER,BOOL +VOID:UINT,UINT,UINT,UINT,POINTER +VOID:UINT64 diff --git a/gst/rtpmanager/gstrtpbin.c b/gst/rtpmanager/gstrtpbin.c index 560bf5f954..e8d659ff0c 100644 --- a/gst/rtpmanager/gstrtpbin.c +++ b/gst/rtpmanager/gstrtpbin.c @@ -58,6 +58,11 @@ * mapping. One can clear the cached values with the #GstRtpSession::clear-pt-map * signal. * + * Access to the internal statistics of gstrtpbin is provided with the + * get-internal-session property. This action signal gives access to the + * RTPSession object which further provides action signals to retrieve the + * internal source and other sources. + * * * Example pipelines * |[ @@ -1331,10 +1336,12 @@ free_stream (GstRtpBinStream * stream) g_signal_handler_disconnect (stream->buffer, stream->buffer_ptreq_sig); g_signal_handler_disconnect (stream->buffer, stream->buffer_ntpstop_sig); - gst_element_set_locked_state (stream->demux, TRUE); + if (stream->demux) + gst_element_set_locked_state (stream->demux, TRUE); gst_element_set_locked_state (stream->buffer, TRUE); - gst_element_set_state (stream->demux, GST_STATE_NULL); + if (stream->demux) + gst_element_set_state (stream->demux, GST_STATE_NULL); gst_element_set_state (stream->buffer, GST_STATE_NULL); /* now remove this signal, we need this while going to NULL because it to @@ -1762,11 +1769,13 @@ gst_rtp_bin_set_sdes_struct (GstRtpBin * bin, const GstStructure * sdes) if (bin->sdes) gst_structure_free (bin->sdes); bin->sdes = gst_structure_copy (sdes); + GST_OBJECT_UNLOCK (bin); /* store in all sessions */ - for (item = bin->sessions; item; item = g_slist_next (item)) - g_object_set (item->data, "sdes", sdes, NULL); - GST_OBJECT_UNLOCK (bin); + for (item = bin->sessions; item; item = g_slist_next (item)) { + GstRtpBinSession *session = item->data; + g_object_set (session->session, "sdes", sdes, NULL); + } GST_RTP_BIN_UNLOCK (bin); } @@ -1927,8 +1936,13 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message) GstRtpBinStream *stream; gboolean change = FALSE, active = FALSE; GstClockTime min_out_time; + GstBufferingMode mode; + gint avg_in, avg_out; + gint64 buffering_left; gst_message_parse_buffering (message, &percent); + gst_message_parse_buffering_stats (message, &mode, &avg_in, &avg_out, + &buffering_left); stream = g_object_get_data (G_OBJECT (GST_MESSAGE_SRC (message)), @@ -1990,6 +2004,8 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message) /* make a new buffering message with the min value */ message = gst_message_new_buffering (GST_OBJECT_CAST (bin), min_percent); + gst_message_set_buffering_stats (message, mode, avg_in, avg_out, + buffering_left); if (G_UNLIKELY (change)) { GstClock *clock; diff --git a/gst/rtpmanager/gstrtpjitterbuffer.c b/gst/rtpmanager/gstrtpjitterbuffer.c index 918238ce7b..fcaef015a1 100644 --- a/gst/rtpmanager/gstrtpjitterbuffer.c +++ b/gst/rtpmanager/gstrtpjitterbuffer.c @@ -235,6 +235,7 @@ static GstPad *gst_rtp_jitter_buffer_request_new_pad (GstElement * element, GstPadTemplate * templ, const gchar * name); static void gst_rtp_jitter_buffer_release_pad (GstElement * element, GstPad * pad); +static GstClock *gst_rtp_jitter_buffer_provide_clock (GstElement * element); /* pad overrides */ static GstCaps *gst_rtp_jitter_buffer_getcaps (GstPad * pad); @@ -439,6 +440,8 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass) GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_request_new_pad); gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_release_pad); + gstelement_class->provide_clock = + GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_provide_clock); klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_clear_pt_map); klass->set_active = GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_set_active); @@ -651,6 +654,12 @@ wrong_pad: } } +static GstClock * +gst_rtp_jitter_buffer_provide_clock (GstElement * element) +{ + return gst_system_clock_obtain (); +} + static void gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer) { @@ -1118,6 +1127,7 @@ newseg_wrong_format: { GST_DEBUG_OBJECT (jitterbuffer, "received non TIME newsegment"); ret = FALSE; + gst_event_unref (event); goto done; } } diff --git a/gst/rtpmanager/gstrtpptdemux.c b/gst/rtpmanager/gstrtpptdemux.c index 6b49483fb9..78c4956d38 100644 --- a/gst/rtpmanager/gstrtpptdemux.c +++ b/gst/rtpmanager/gstrtpptdemux.c @@ -132,6 +132,9 @@ static void gst_rtp_pt_demux_clear_pt_map (GstRtpPtDemux * rtpdemux); static GstRtpPtDemuxPad *find_pad_for_pt (GstRtpPtDemux * rtpdemux, guint8 pt); +static gboolean gst_rtp_pt_demux_src_event (GstPad * pad, GstEvent * event); + + static guint gst_rtp_pt_demux_signals[LAST_SIGNAL] = { 0 }; static void @@ -329,6 +332,7 @@ gst_rtp_pt_demux_chain (GstPad * pad, GstBuffer * buf) srcpad = gst_pad_new_from_template (templ, padname); gst_pad_use_fixed_caps (srcpad); g_free (padname); + gst_pad_set_event_function (srcpad, gst_rtp_pt_demux_src_event); caps = gst_rtp_pt_demux_get_caps (rtpdemux, pt); if (!caps) @@ -462,6 +466,47 @@ gst_rtp_pt_demux_sink_event (GstPad * pad, GstEvent * event) } +static gboolean +gst_rtp_pt_demux_src_event (GstPad * pad, GstEvent * event) +{ + GstRtpPtDemux *demux; + const GstStructure *s; + + demux = GST_RTP_PT_DEMUX (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CUSTOM_UPSTREAM: + case GST_EVENT_CUSTOM_BOTH: + case GST_EVENT_CUSTOM_BOTH_OOB: + s = gst_event_get_structure (event); + if (s && !gst_structure_has_field (s, "payload")) { + GSList *walk; + + for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) { + GstRtpPtDemuxPad *dpad = (GstRtpPtDemuxPad *) walk->data; + + if (dpad->pad == pad) { + event = + GST_EVENT_CAST (gst_mini_object_make_writable + (GST_MINI_OBJECT_CAST (event))); + gst_structure_set (event->structure, + "payload", G_TYPE_UINT, dpad->pt, NULL); + break; + } + } + } + break; + default: + break; + } + + gst_object_unref (demux); + + return gst_pad_event_default (pad, event); +} + + + /* * Reserves resources for the object. */ diff --git a/gst/rtpmanager/gstrtpsession.c b/gst/rtpmanager/gstrtpsession.c index fd2663652e..64abc94ba6 100644 --- a/gst/rtpmanager/gstrtpsession.c +++ b/gst/rtpmanager/gstrtpsession.c @@ -199,6 +199,7 @@ enum #define DEFAULT_NUM_SOURCES 0 #define DEFAULT_NUM_ACTIVE_SOURCES 0 #define DEFAULT_USE_PIPELINE_CLOCK FALSE +#define DEFAULT_RTCP_MIN_INTERVAL (RTP_STATS_MIN_INTERVAL * GST_SECOND) enum { @@ -213,6 +214,7 @@ enum PROP_NUM_ACTIVE_SOURCES, PROP_INTERNAL_SESSION, PROP_USE_PIPELINE_CLOCK, + PROP_RTCP_MIN_INTERVAL, PROP_LAST }; @@ -255,6 +257,10 @@ static GstFlowReturn gst_rtp_session_sync_rtcp (RTPSession * sess, static gint gst_rtp_session_clock_rate (RTPSession * sess, guint8 payload, gpointer user_data); static void gst_rtp_session_reconsider (RTPSession * sess, gpointer user_data); +static void gst_rtp_session_request_key_unit (RTPSession * sess, + gboolean all_headers, gpointer user_data); +static GstClockTime gst_rtp_session_request_time (RTPSession * session, + gpointer user_data); static RTPSessionCallbacks callbacks = { gst_rtp_session_process_rtp, @@ -262,7 +268,9 @@ static RTPSessionCallbacks callbacks = { gst_rtp_session_sync_rtcp, gst_rtp_session_send_rtcp, gst_rtp_session_clock_rate, - gst_rtp_session_reconsider + gst_rtp_session_reconsider, + gst_rtp_session_request_key_unit, + gst_rtp_session_request_time }; /* GObject vmethods */ @@ -588,6 +596,12 @@ gst_rtp_session_class_init (GstRtpSessionClass * klass) DEFAULT_USE_PIPELINE_CLOCK, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_RTCP_MIN_INTERVAL, + g_param_spec_uint64 ("rtcp-min-interval", "Minimum RTCP interval", + "Minimum interval between Regular RTCP packet (in ns)", + 0, G_MAXUINT64, DEFAULT_RTCP_MIN_INTERVAL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_session_change_state); gstelement_class->request_new_pad = @@ -693,6 +707,10 @@ gst_rtp_session_set_property (GObject * object, guint prop_id, case PROP_USE_PIPELINE_CLOCK: priv->use_pipeline_clock = g_value_get_boolean (value); break; + case PROP_RTCP_MIN_INTERVAL: + g_object_set_property (G_OBJECT (priv->session), "rtcp-min-interval", + value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -745,6 +763,10 @@ gst_rtp_session_get_property (GObject * object, guint prop_id, case PROP_USE_PIPELINE_CLOCK: g_value_set_boolean (value, priv->use_pipeline_clock); break; + case PROP_RTCP_MIN_INTERVAL: + g_object_get_property (G_OBJECT (priv->session), "rtcp-min-interval", + value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1215,9 +1237,13 @@ gst_rtp_session_get_caps_for_pt (GstRtpSession * rtpsession, guint payload) g_value_init (&ret, GST_TYPE_CAPS); g_value_set_boxed (&ret, NULL); + GST_RTP_SESSION_UNLOCK (rtpsession); + g_signal_emitv (args, gst_rtp_session_signals[SIGNAL_REQUEST_PT_MAP], 0, &ret); + GST_RTP_SESSION_LOCK (rtpsession); + g_value_unset (&args[0]); g_value_unset (&args[1]); caps = (GstCaps *) g_value_dup_boxed (&ret); @@ -1350,6 +1376,90 @@ gst_rtp_session_event_recv_rtp_sink (GstPad * pad, GstEvent * event) } +static gboolean +gst_rtp_session_request_remote_key_unit (GstRtpSession * rtpsession, + guint32 ssrc, guint payload, gboolean all_headers) +{ + GstCaps *caps; + gboolean requested = FALSE; + + caps = gst_rtp_session_get_caps_for_pt (rtpsession, payload); + + if (caps) { + gboolean fir, pli; + const GstStructure *s = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_boolean (s, "rtcp-fb-nack-fir", &fir)) + fir = FALSE; + + if (!gst_structure_get_boolean (s, "rtcp-fb-nack-pli", &pli)) + pli = FALSE; + + gst_caps_unref (caps); + + if (!pli && !fir) + goto out; + + /* When we need all headers, use FIR if possible falling back to PLI if + * it's available */ + if (all_headers) { + /* 500 ms acceptable delay for urgent request is a guesstimate, it could + * be made configurable if needed + */ + /* If we don't have fir, fall back to pli */ + rtp_session_request_key_unit (rtpsession->priv->session, ssrc, fir); + rtp_session_request_early_rtcp (rtpsession->priv->session, + gst_clock_get_time (rtpsession->priv->sysclock), 500 * GST_MSECOND); + requested = TRUE; + } else if (pli) { + rtp_session_request_key_unit (rtpsession->priv->session, ssrc, FALSE); + requested = TRUE; + } + } + +out: + return requested; +} + +static gboolean +gst_rtp_session_event_recv_rtp_src (GstPad * pad, GstEvent * event) +{ + GstRtpSession *rtpsession; + gboolean forward = TRUE; + gboolean ret = TRUE; + const GstStructure *s; + guint32 ssrc; + guint pt; + + rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CUSTOM_UPSTREAM: + s = gst_event_get_structure (event); + if (gst_structure_has_name (s, "GstForceKeyUnit") && + gst_structure_get_uint (s, "ssrc", &ssrc) && + gst_structure_get_uint (s, "payload", &pt)) { + gboolean all_headers = FALSE; + + gst_structure_get_boolean (s, "all-headers", &all_headers); + if (gst_rtp_session_request_remote_key_unit (rtpsession, ssrc, pt, + all_headers)) + forward = FALSE; + } + break; + default: + break; + } + + if (forward) + ret = gst_pad_push_event (rtpsession->recv_rtp_sink, event); + + gst_object_unref (rtpsession); + + return ret; +} + + static GstIterator * gst_rtp_session_iterate_internal_links (GstPad * pad) { @@ -1471,6 +1581,7 @@ gst_rtp_session_chain_recv_rtcp (GstPad * pad, GstBuffer * buffer) GstRtpSession *rtpsession; GstRtpSessionPrivate *priv; GstClockTime current_time; + guint64 ntpnstime; GstFlowReturn ret; rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad)); @@ -1479,7 +1590,10 @@ gst_rtp_session_chain_recv_rtcp (GstPad * pad, GstBuffer * buffer) GST_LOG_OBJECT (rtpsession, "received RTCP packet"); current_time = gst_clock_get_time (priv->sysclock); - ret = rtp_session_process_rtcp (priv->session, buffer, current_time); + get_current_times (rtpsession, NULL, &ntpnstime); + + ret = + rtp_session_process_rtcp (priv->session, buffer, current_time, ntpnstime); gst_object_unref (rtpsession); @@ -1760,6 +1874,8 @@ create_recv_rtp_sink (GstRtpSession * rtpsession) rtpsession->recv_rtp_src = gst_pad_new_from_static_template (&rtpsession_recv_rtp_src_template, "recv_rtp_src"); + gst_pad_set_event_function (rtpsession->recv_rtp_src, + (GstPadEventFunction) gst_rtp_session_event_recv_rtp_src); gst_pad_set_iterate_internal_links_function (rtpsession->recv_rtp_src, gst_rtp_session_iterate_internal_links); gst_pad_use_fixed_caps (rtpsession->recv_rtp_src); @@ -2037,3 +2153,24 @@ wrong_pad: return; } } + +static void +gst_rtp_session_request_key_unit (RTPSession * sess, + gboolean all_headers, gpointer user_data) +{ + GstRtpSession *rtpsession = GST_RTP_SESSION (user_data); + GstEvent *event; + + event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, + gst_structure_new ("GstForceKeyUnit", + "all-headers", G_TYPE_BOOLEAN, all_headers, NULL)); + gst_pad_push_event (rtpsession->send_rtp_sink, event); +} + +static GstClockTime +gst_rtp_session_request_time (RTPSession * session, gpointer user_data) +{ + GstRtpSession *rtpsession = GST_RTP_SESSION (user_data); + + return gst_clock_get_time (rtpsession->priv->sysclock); +} diff --git a/gst/rtpmanager/gstrtpssrcdemux.c b/gst/rtpmanager/gstrtpssrcdemux.c index c8348da51e..65d4475202 100644 --- a/gst/rtpmanager/gstrtpssrcdemux.c +++ b/gst/rtpmanager/gstrtpssrcdemux.c @@ -208,6 +208,7 @@ create_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc, gst_rtp_ssrc_demux_iterate_internal_links); gst_pad_set_active (rtp_pad, TRUE); + gst_pad_set_event_function (rtcp_pad, gst_rtp_ssrc_demux_src_event); gst_pad_set_iterate_internal_links_function (rtcp_pad, gst_rtp_ssrc_demux_iterate_internal_links); gst_pad_set_active (rtcp_pad, TRUE); @@ -621,18 +622,39 @@ static gboolean gst_rtp_ssrc_demux_src_event (GstPad * pad, GstEvent * event) { GstRtpSsrcDemux *demux; - gboolean res = FALSE; + const GstStructure *s; demux = GST_RTP_SSRC_DEMUX (gst_pad_get_parent (pad)); switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: + case GST_EVENT_CUSTOM_UPSTREAM: + case GST_EVENT_CUSTOM_BOTH: + case GST_EVENT_CUSTOM_BOTH_OOB: + s = gst_event_get_structure (event); + if (s && !gst_structure_has_field (s, "ssrc")) { + GSList *walk; + + for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) { + GstRtpSsrcDemuxPad *dpad = (GstRtpSsrcDemuxPad *) walk->data; + + if (dpad->rtp_pad == pad || dpad->rtcp_pad == pad) { + event = + GST_EVENT_CAST (gst_mini_object_make_writable + (GST_MINI_OBJECT_CAST (event))); + gst_structure_set (event->structure, "ssrc", G_TYPE_UINT, + dpad->ssrc, NULL); + break; + } + } + } + break; default: - res = gst_pad_event_default (pad, event); break; } + gst_object_unref (demux); - return res; + + return gst_pad_event_default (pad, event); } static GstIterator * diff --git a/gst/rtpmanager/rtpjitterbuffer.c b/gst/rtpmanager/rtpjitterbuffer.c index 2dee30c071..341388bd25 100644 --- a/gst/rtpmanager/rtpjitterbuffer.c +++ b/gst/rtpmanager/rtpjitterbuffer.c @@ -212,11 +212,30 @@ rtp_jitter_buffer_resync (RTPJitterBuffer * jbuf, GstClockTime time, static guint64 get_buffer_level (RTPJitterBuffer * jbuf) { - GstBuffer *high_buf, *low_buf; + GstBuffer *high_buf = NULL, *low_buf = NULL; guint64 level; + GList *find; - high_buf = g_queue_peek_head (jbuf->packets); - low_buf = g_queue_peek_tail (jbuf->packets); + /* first first buffer with timestamp */ + find = g_queue_peek_head_link (jbuf->packets); + while (find) { + high_buf = find->data; + if (GST_BUFFER_TIMESTAMP (high_buf) != -1) + break; + + high_buf = NULL; + find = g_list_next (find); + } + + find = g_queue_peek_tail_link (jbuf->packets); + while (find) { + low_buf = find->data; + if (GST_BUFFER_TIMESTAMP (low_buf) != -1) + break; + + low_buf = NULL; + find = g_list_previous (find); + } if (!high_buf || !low_buf || high_buf == low_buf) { level = 0; @@ -230,6 +249,11 @@ get_buffer_level (RTPJitterBuffer * jbuf) level = high_ts - low_ts; else level = 0; + + GST_LOG_OBJECT (jbuf, + "low %" GST_TIME_FORMAT " high %" GST_TIME_FORMAT " level %" + G_GUINT64_FORMAT, GST_TIME_ARGS (low_ts), GST_TIME_ARGS (high_ts), + level); } return level; } @@ -259,7 +283,7 @@ update_buffer_level (RTPJitterBuffer * jbuf, gint * percent) if (post) { gint perc; - if (jbuf->buffering) { + if (jbuf->buffering && (jbuf->high_level != 0)) { perc = (level * 100 / jbuf->high_level); perc = MIN (perc, 100); } else { @@ -394,6 +418,8 @@ calculate_skew (RTPJitterBuffer * jbuf, guint32 rtptime, GstClockTime time, } else { GST_WARNING ("backward timestamps at server but no timestamps"); send_diff = 0; + /* at least try to get a new timestamp.. */ + jbuf->base_time = -1; } GST_DEBUG ("extrtp %" G_GUINT64_FORMAT ", gstrtp %" GST_TIME_FORMAT ", base %" diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index 3ca79660d5..a455ad3564 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -42,6 +42,9 @@ enum SIGNAL_ON_BYE_TIMEOUT, SIGNAL_ON_TIMEOUT, SIGNAL_ON_SENDER_TIMEOUT, + SIGNAL_ON_SENDING_RTCP, + SIGNAL_ON_FEEDBACK_RTCP, + SIGNAL_SEND_RTCP, LAST_SIGNAL }; @@ -55,6 +58,8 @@ enum #define DEFAULT_NUM_SOURCES 0 #define DEFAULT_NUM_ACTIVE_SOURCES 0 #define DEFAULT_SOURCES NULL +#define DEFAULT_RTCP_MIN_INTERVAL (RTP_STATS_MIN_INTERVAL * GST_SECOND) +#define DEFAULT_RTCP_FEEDBACK_RETENTION_WINDOW (2 * GST_SECOND) enum { @@ -71,16 +76,20 @@ enum PROP_NUM_ACTIVE_SOURCES, PROP_SOURCES, PROP_FAVOR_NEW, + PROP_RTCP_MIN_INTERVAL, + PROP_RTCP_FEEDBACK_RETENTION_WINDOW, PROP_LAST }; -/* update average packet size, we keep this scaled by 16 to keep enough - * precision. */ +/* update average packet size */ +#define INIT_AVG(avg, val) \ + (avg) = (val); #define UPDATE_AVG(avg, val) \ if ((avg) == 0) \ - (avg) = (val) << 4; \ + (avg) = (val); \ else \ - (avg) = ((val) + (15 * (avg))); + (avg) = ((val) + (15 * (avg))) >> 4; + /* The number RTCP intervals after which to timeout entries in the * collision table @@ -94,6 +103,12 @@ static void rtp_session_set_property (GObject * object, guint prop_id, static void rtp_session_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); +static gboolean rtp_session_on_sending_rtcp (RTPSession * sess, + GstBuffer * buffer, gboolean early); +static void rtp_session_send_rtcp (RTPSession * sess, + GstClockTimeDiff max_delay); + + static guint rtp_session_signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE (RTPSession, rtp_session, G_TYPE_OBJECT); @@ -105,6 +120,16 @@ static GstFlowReturn rtp_session_schedule_bye_locked (RTPSession * sess, static GstClockTime calculate_rtcp_interval (RTPSession * sess, gboolean deterministic, gboolean first); +static gboolean +accumulate_trues (GSignalInvocationHint * ihint, GValue * return_accu, + const GValue * handler_return, gpointer data) +{ + if (g_value_get_boolean (handler_return)) + g_value_set_boolean (return_accu, TRUE); + + return TRUE; +} + static void rtp_session_class_init (RTPSessionClass * klass) { @@ -238,6 +263,61 @@ rtp_session_class_init (RTPSessionClass * klass) NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, RTP_TYPE_SOURCE); + /** + * RTPSession::on-sending-rtcp + * @session: the object which received the signal + * @buffer: the #GstBuffer containing the RTCP packet about to be sent + * @early: %TRUE if the packet is early, %FALSE if it is regular + * + * This signal is emitted before sending an RTCP packet, it can be used + * to add extra RTCP Packets. + * + * Returns: %TRUE if the RTCP buffer should NOT be suppressed, %FALSE + * if suppressing it is acceptable + */ + rtp_session_signals[SIGNAL_ON_SENDING_RTCP] = + g_signal_new ("on-sending-rtcp", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_sending_rtcp), + accumulate_trues, NULL, gst_rtp_bin_marshal_BOOLEAN__POINTER_BOOLEAN, + G_TYPE_BOOLEAN, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN); + + /** + * RTPSession::on-feedback-rtcp: + * @session: the object which received the signal + * @type: Type of RTCP packet, will be %GST_RTCP_TYPE_RTPFB or + * %GST_RTCP_TYPE_RTPFB + * @fbtype: The type of RTCP FB packet, probably part of #GstRTCPFBType + * @sender_ssrc: The SSRC of the sender + * @media_ssrc: The SSRC of the media this refers to + * @fci: a #GstBuffer with the FCI data from the FB packet or %NULL if + * there was no FCI + * + * Notify that a RTCP feedback packet has been received + */ + + rtp_session_signals[SIGNAL_ON_FEEDBACK_RTCP] = + g_signal_new ("on-feedback-rtcp", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_feedback_rtcp), + NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT_UINT_UINT_POINTER, + G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, + G_TYPE_POINTER); + + /** + * RTPSession::send-rtcp: + * @session: the object which received the signal + * @max_delay: The maximum delay after which the feedback will not be useful + * anymore + * + * Requests that the #RTPSession initiate a new RTCP packet as soon as + * possible within the requested delay. + */ + + rtp_session_signals[SIGNAL_SEND_RTCP] = + g_signal_new ("send-rtcp", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (RTPSessionClass, send_rtcp), NULL, NULL, + gst_rtp_bin_marshal_VOID__UINT64, G_TYPE_NONE, 1, G_TYPE_UINT64); + g_object_class_install_property (gobject_class, PROP_INTERNAL_SSRC, g_param_spec_uint ("internal-ssrc", "Internal SSRC", "The internal SSRC used for the session", @@ -329,9 +409,25 @@ rtp_session_class_init (RTPSessionClass * klass) "Resolve SSRC conflict in favor of new sources", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_RTCP_MIN_INTERVAL, + g_param_spec_uint64 ("rtcp-min-interval", "Minimum RTCP interval", + "Minimum interval between Regular RTCP packet (in ns)", + 0, G_MAXUINT64, DEFAULT_RTCP_MIN_INTERVAL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_RTCP_FEEDBACK_RETENTION_WINDOW, + g_param_spec_uint64 ("rtcp-feedback-retention-window", + "RTCP Feedback retention window", + "Duration during which RTCP Feedback packets are retained (in ns)", + 0, G_MAXUINT64, DEFAULT_RTCP_FEEDBACK_RETENTION_WINDOW, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + klass->get_source_by_ssrc = GST_DEBUG_FUNCPTR (rtp_session_get_source_by_ssrc); + klass->on_sending_rtcp = GST_DEBUG_FUNCPTR (rtp_session_on_sending_rtcp); + klass->send_rtcp = GST_DEBUG_FUNCPTR (rtp_session_send_rtcp); GST_DEBUG_CATEGORY_INIT (rtp_session_debug, "rtpsession", 0, "RTP Session"); } @@ -367,6 +463,7 @@ rtp_session_init (RTPSession * sess) sess->source->validated = TRUE; sess->source->internal = TRUE; sess->stats.active_sources++; + INIT_AVG (sess->stats.avg_rtcp_packet_size, 100); /* default UDP header length */ sess->header_len = 28; @@ -382,6 +479,10 @@ rtp_session_init (RTPSession * sess) rtp_source_set_sdes_string (sess->source, GST_RTCP_SDES_TOOL, "GStreamer"); sess->first_rtcp = TRUE; + sess->allow_early = TRUE; + sess->rtcp_feedback_retention_window = DEFAULT_RTCP_FEEDBACK_RETENTION_WINDOW; + + sess->rtcp_pli_requests = g_array_new (FALSE, FALSE, sizeof (guint32)); GST_DEBUG ("%p: session using SSRC: %08x", sess, sess->source->ssrc); } @@ -403,6 +504,8 @@ rtp_session_finalize (GObject * object) g_hash_table_destroy (sess->cnames); g_object_unref (sess->source); + g_array_free (sess->rtcp_pli_requests, TRUE); + G_OBJECT_CLASS (rtp_session_parent_class)->finalize (object); } @@ -473,6 +576,10 @@ rtp_session_set_property (GObject * object, guint prop_id, case PROP_FAVOR_NEW: sess->favor_new = g_value_get_boolean (value); break; + case PROP_RTCP_MIN_INTERVAL: + rtp_stats_set_min_interval (&sess->stats, + (gdouble) g_value_get_uint64 (value) / GST_SECOND); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -524,6 +631,9 @@ rtp_session_get_property (GObject * object, guint prop_id, case PROP_FAVOR_NEW: g_value_set_boolean (value, sess->favor_new); break; + case PROP_RTCP_MIN_INTERVAL: + g_value_set_uint64 (value, sess->stats.min_interval * GST_SECOND); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -679,6 +789,14 @@ rtp_session_set_callbacks (RTPSession * sess, RTPSessionCallbacks * callbacks, sess->callbacks.reconsider = callbacks->reconsider; sess->reconsider_user_data = user_data; } + if (callbacks->request_key_unit) { + sess->callbacks.request_key_unit = callbacks->request_key_unit; + sess->request_key_unit_user_data = user_data; + } + if (callbacks->request_time) { + sess->callbacks.request_time = callbacks->request_time; + sess->request_time_user_data = user_data; + } } /** @@ -789,6 +907,24 @@ rtp_session_set_reconsider_callback (RTPSession * sess, sess->reconsider_user_data = user_data; } +/** + * rtp_session_set_request_time_callback: + * @sess: an #RTPSession + * @callback: callback to set + * @user_data: user data passed in the callback + * + * Configure only the request_time callback + */ +void +rtp_session_set_request_time_callback (RTPSession * sess, + RTPSessionRequestTime callback, gpointer user_data) +{ + g_return_if_fail (RTP_IS_SESSION (sess)); + + sess->callbacks.request_time = callback; + sess->request_time_user_data = user_data; +} + /** * rtp_session_set_bandwidth: * @sess: an #RTPSession @@ -1462,11 +1598,12 @@ rtp_session_create_source (RTPSession * sess) static void update_arrival_stats (RTPSession * sess, RTPArrivalStats * arrival, gboolean rtp, GstBuffer * buffer, GstClockTime current_time, - GstClockTime running_time) + GstClockTime running_time, guint64 ntpnstime) { /* get time of arrival */ arrival->current_time = current_time; arrival->running_time = running_time; + arrival->ntpnstime = ntpnstime; /* get packet size including header overhead */ arrival->bytes = GST_BUFFER_SIZE (buffer) + sess->header_len; @@ -1521,7 +1658,7 @@ rtp_session_process_rtp (RTPSession * sess, GstBuffer * buffer, RTP_SESSION_LOCK (sess); /* update arrival stats */ update_arrival_stats (sess, &arrival, TRUE, buffer, current_time, - running_time); + running_time, -1); /* ignore more RTP packets when we left the session */ if (sess->source->received_bye) @@ -1642,12 +1779,11 @@ rtp_session_process_rb (RTPSession * sess, RTPSource * source, /* only deal with report blocks for our session, we update the stats of * the sender of the RTCP message. We could also compare our stats against * the other sender to see if we are better or worse. */ - rtp_source_process_rb (source, arrival->current_time, fractionlost, + rtp_source_process_rb (source, arrival->ntpnstime, fractionlost, packetslost, exthighestseq, jitter, lsr, dlsr); - - on_ssrc_active (sess, source); } } + on_ssrc_active (sess, source); } /* A Sender report contains statistics about how the sender is doing. This @@ -1747,7 +1883,7 @@ rtp_session_process_sdes (RTPSession * sess, GstRTCPPacket * packet, i = 0; while (more_items) { guint32 ssrc; - gboolean changed, created; + gboolean changed, created, validated; RTPSource *source; GstStructure *sdes; @@ -1800,10 +1936,13 @@ rtp_session_process_sdes (RTPSession * sess, GstRTCPPacket * packet, /* takes ownership of sdes */ changed = rtp_source_set_sdes_struct (source, sdes); + validated = !RTP_SOURCE_IS_ACTIVE (source); source->validated = TRUE; if (created) on_new_ssrc (sess, source); + if (validated) + on_ssrc_validated (sess, source); if (changed) on_ssrc_sdes (sess, source); @@ -1911,11 +2050,111 @@ rtp_session_process_app (RTPSession * sess, GstRTCPPacket * packet, GST_DEBUG ("received APP"); } +static void +rtp_session_process_pli (RTPSession * sess, guint32 sender_ssrc, + guint32 media_ssrc, GstClockTime current_time) +{ + RTPSource *src; + guint32 round_trip = 0; + + if (!sess->callbacks.request_key_unit) + return; + + src = g_hash_table_lookup (sess->ssrcs[sess->mask_idx], + GINT_TO_POINTER (sender_ssrc)); + + if (!src) + return; + + if (sess->last_keyframe_request != GST_CLOCK_TIME_NONE && + rtp_source_get_last_rb (src, NULL, NULL, NULL, NULL, NULL, NULL, + &round_trip)) { + GstClockTime round_trip_in_ns = gst_util_uint64_scale (round_trip, + GST_SECOND, 65536); + + if (sess->last_keyframe_request != GST_CLOCK_TIME_NONE && + current_time - sess->last_keyframe_request < round_trip_in_ns) { + GST_DEBUG ("Ignoring PLI because one was send without one RTT (%" + GST_TIME_FORMAT " < %" GST_TIME_FORMAT ")", + GST_TIME_ARGS (current_time - sess->last_keyframe_request), + GST_TIME_ARGS (round_trip_in_ns));; + return; + } + } + + sess->last_keyframe_request = current_time; + + GST_LOG ("received PLI from %X %p(%p)", sender_ssrc, + sess->callbacks.process_rtp, sess->callbacks.request_key_unit); + + sess->callbacks.request_key_unit (sess, FALSE, + sess->request_key_unit_user_data); +} + +static void +rtp_session_process_feedback (RTPSession * sess, GstRTCPPacket * packet, + RTPArrivalStats * arrival, GstClockTime current_time) +{ + GstRTCPType type = gst_rtcp_packet_get_type (packet); + GstRTCPFBType fbtype = gst_rtcp_packet_fb_get_type (packet); + guint32 sender_ssrc = gst_rtcp_packet_fb_get_sender_ssrc (packet); + guint32 media_ssrc = gst_rtcp_packet_fb_get_media_ssrc (packet); + guint length = 4 * (gst_rtcp_packet_get_length (packet) - 2); + + GST_DEBUG ("received feedback %d:%d from %08X about %08X" + " with FCI of length %d", type, fbtype, sender_ssrc, media_ssrc, length); + + if (g_signal_has_handler_pending (sess, + rtp_session_signals[SIGNAL_ON_FEEDBACK_RTCP], 0, TRUE)) { + GstBuffer *fci = NULL; + + if (length) { + fci = gst_buffer_create_sub (packet->buffer, packet->offset + 72, length); + GST_BUFFER_TIMESTAMP (fci) = arrival->running_time; + } + + RTP_SESSION_UNLOCK (sess); + g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_FEEDBACK_RTCP], 0, + type, fbtype, sender_ssrc, media_ssrc, fci); + RTP_SESSION_LOCK (sess); + + if (fci) + gst_buffer_unref (fci); + } + + if (sess->rtcp_feedback_retention_window) { + RTPSource *src = g_hash_table_lookup (sess->ssrcs[sess->mask_idx], + GINT_TO_POINTER (media_ssrc)); + + if (src) + rtp_source_retain_rtcp_packet (src, packet, arrival->running_time); + } + + if (rtp_source_get_ssrc (sess->source) == media_ssrc) { + switch (type) { + case GST_RTCP_TYPE_PSFB: + switch (fbtype) { + case GST_RTCP_PSFB_TYPE_PLI: + rtp_session_process_pli (sess, sender_ssrc, media_ssrc, + current_time); + break; + default: + break; + } + break; + case GST_RTCP_TYPE_RTPFB: + default: + break; + } + } +} + /** * rtp_session_process_rtcp: * @sess: and #RTPSession * @buffer: an RTCP buffer * @current_time: the current system time + * @ntpnstime: the current NTP time in nanoseconds * * Process an RTCP buffer in the session manager. This function takes ownership * of @buffer. @@ -1924,7 +2163,7 @@ rtp_session_process_app (RTPSession * sess, GstRTCPPacket * packet, */ GstFlowReturn rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer, - GstClockTime current_time) + GstClockTime current_time, guint64 ntpnstime) { GstRTCPPacket packet; gboolean more, is_bye = FALSE, do_sync = FALSE; @@ -1941,14 +2180,12 @@ rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer, RTP_SESSION_LOCK (sess); /* update arrival stats */ - update_arrival_stats (sess, &arrival, FALSE, buffer, current_time, -1); + update_arrival_stats (sess, &arrival, FALSE, buffer, current_time, -1, + ntpnstime); if (sess->sent_bye) goto ignore; - /* make writable, we might want to change the buffer */ - buffer = gst_buffer_make_metadata_writable (buffer); - /* start processing the compound packet */ more = gst_rtcp_buffer_get_first_packet (buffer, &packet); while (more) { @@ -1981,6 +2218,10 @@ rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer, case GST_RTCP_TYPE_APP: rtp_session_process_app (sess, &packet, &arrival); break; + case GST_RTCP_TYPE_RTPFB: + case GST_RTCP_TYPE_PSFB: + rtp_session_process_feedback (sess, &packet, &arrival, current_time); + break; default: GST_WARNING ("got unknown RTCP packet"); break; @@ -2000,13 +2241,18 @@ rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer, /* keep track of average packet size */ UPDATE_AVG (sess->stats.avg_rtcp_packet_size, arrival.bytes); } + GST_DEBUG ("%p, received RTCP packet, avg size %u, %u", &sess->stats, + sess->stats.avg_rtcp_packet_size, arrival.bytes); RTP_SESSION_UNLOCK (sess); /* notify caller of sr packets in the callback */ - if (do_sync && sess->callbacks.sync_rtcp) + if (do_sync && sess->callbacks.sync_rtcp) { + /* make writable, we might want to change the buffer */ + buffer = gst_buffer_make_metadata_writable (buffer); + result = sess->callbacks.sync_rtcp (sess, sess->source, buffer, sess->sync_rtcp_user_data); - else + } else gst_buffer_unref (buffer); return result; @@ -2169,11 +2415,11 @@ rtp_session_schedule_bye_locked (RTPSession * sess, const gchar * reason, /* at least one member wants to send a BYE */ g_free (sess->bye_reason); sess->bye_reason = g_strdup (reason); - /* The avg packet size is kept scaled by 16 */ - sess->stats.avg_rtcp_packet_size = 100 * 16; + INIT_AVG (sess->stats.avg_rtcp_packet_size, 100); sess->stats.bye_members = 1; sess->first_rtcp = TRUE; sess->sent_bye = FALSE; + sess->allow_early = TRUE; /* reschedule transmission */ sess->last_rtcp_send_time = current_time; @@ -2237,6 +2483,11 @@ rtp_session_next_timeout (RTPSession * sess, GstClockTime current_time) RTP_SESSION_LOCK (sess); + if (GST_CLOCK_TIME_IS_VALID (sess->next_early_rtcp_time)) { + result = sess->next_early_rtcp_time; + goto early_exit; + } + result = sess->next_rtcp_check_time; GST_DEBUG ("current time: %" GST_TIME_FORMAT ", next :%" GST_TIME_FORMAT, @@ -2277,7 +2528,11 @@ rtp_session_next_timeout (RTPSession * sess, GstClockTime current_time) sess->next_rtcp_check_time = result; - GST_DEBUG ("next timeout: %" GST_TIME_FORMAT, GST_TIME_ARGS (result)); +early_exit: + + GST_DEBUG ("current time: %" GST_TIME_FORMAT + ", next time: %" GST_TIME_FORMAT, + GST_TIME_ARGS (current_time), GST_TIME_ARGS (result)); RTP_SESSION_UNLOCK (sess); return result; @@ -2294,6 +2549,8 @@ typedef struct GstRTCPPacket packet; gboolean is_bye; gboolean has_sdes; + gboolean is_early; + gboolean may_suppress; } ReportData; static void @@ -2341,6 +2598,9 @@ session_report_blocks (const gchar * key, RTPSource * source, ReportData * data) /* create a new buffer if needed */ if (data->rtcp == NULL) { session_start_rtcp (sess, data); + } else if (data->is_early) { + /* Put a single RR or SR in minimal compound packets */ + return; } if (gst_rtcp_packet_get_rb_count (packet) < GST_RTCP_MAX_RB_COUNT) { /* only report about other sender sources */ @@ -2354,6 +2614,15 @@ session_report_blocks (const gchar * key, RTPSource * source, ReportData * data) rtp_source_get_new_rb (source, data->current_time, &fractionlost, &packetslost, &exthighestseq, &jitter, &lsr, &dlsr); + /* store last generated RR packet */ + source->last_rr.is_valid = TRUE; + source->last_rr.fractionlost = fractionlost; + source->last_rr.packetslost = packetslost; + source->last_rr.exthighestseq = exthighestseq; + source->last_rr.jitter = jitter; + source->last_rr.lsr = lsr; + source->last_rr.dlsr = dlsr; + /* packet is not yet filled, add report block for this source. */ gst_rtcp_packet_add_rb (packet, source->ssrc, fractionlost, packetslost, exthighestseq, jitter, lsr, dlsr); @@ -2463,6 +2732,10 @@ session_sdes (RTPSession * sess, ReportData * data) continue; type = gst_rtcp_sdes_name_to_type (field); + /* Early packets are minimal and only include the CNAME */ + if (data->is_early && type != GST_RTCP_SDES_CNAME) + continue; + if (type > GST_RTCP_SDES_END && type < GST_RTCP_SDES_PRIV) { gst_rtcp_packet_sdes_add_entry (packet, type, strlen (value), (const guint8 *) value); @@ -2520,7 +2793,9 @@ static gboolean is_rtcp_time (RTPSession * sess, GstClockTime current_time, ReportData * data) { GstClockTime new_send_time, elapsed; - gboolean result; + + if (data->is_early && sess->next_early_rtcp_time < current_time) + goto early; /* no need to check yet */ if (sess->next_rtcp_check_time > current_time) { @@ -2545,18 +2820,40 @@ is_rtcp_time (RTPSession * sess, GstClockTime current_time, ReportData * data) if (current_time < new_send_time) { GST_DEBUG ("reconsider RTCP for %" GST_TIME_FORMAT, GST_TIME_ARGS (new_send_time)); - result = FALSE; /* store new check time */ sess->next_rtcp_check_time = new_send_time; - } else { - result = TRUE; - new_send_time = calculate_rtcp_interval (sess, FALSE, FALSE); - - GST_DEBUG ("can send RTCP now, next interval %" GST_TIME_FORMAT, - GST_TIME_ARGS (new_send_time)); - sess->next_rtcp_check_time = current_time + new_send_time; + return FALSE; } - return result; + +early: + + new_send_time = calculate_rtcp_interval (sess, FALSE, FALSE); + + GST_DEBUG ("can send RTCP now, next interval %" GST_TIME_FORMAT, + GST_TIME_ARGS (new_send_time)); + sess->next_rtcp_check_time = current_time + new_send_time; + + /* Apply the rules from RFC 4585 section 3.5.3 */ + if (sess->stats.min_interval != 0 && !sess->first_rtcp) { + GstClockTimeDiff T_rr_current_interval = g_random_double_range (0.5, 1.5) * + sess->stats.min_interval; + + /* This will caused the RTCP to be suppressed if no FB packets are added */ + if (sess->last_rtcp_send_time + T_rr_current_interval > + sess->next_rtcp_check_time) { + GST_DEBUG ("RTCP packet could be suppressed min: %" GST_TIME_FORMAT + " last: %" GST_TIME_FORMAT + " + T_rr_current_interval: %" GST_TIME_FORMAT + " > sess->next_rtcp_check_time: %" GST_TIME_FORMAT, + GST_TIME_ARGS (sess->stats.min_interval), + GST_TIME_ARGS (sess->last_rtcp_send_time), + GST_TIME_ARGS (T_rr_current_interval), + GST_TIME_ARGS (sess->next_rtcp_check_time)); + data->may_suppress = TRUE; + } + } + + return TRUE; } static void @@ -2610,6 +2907,7 @@ rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time, data.ntpnstime = ntpnstime; data.is_bye = FALSE; data.has_sdes = FALSE; + data.may_suppress = FALSE; data.running_time = running_time; own = sess->source; @@ -2634,6 +2932,11 @@ rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time, g_hash_table_foreach_remove (sess->ssrcs[sess->mask_idx], (GHRFunc) remove_closing_sources, NULL); + if (GST_CLOCK_TIME_IS_VALID (sess->next_early_rtcp_time)) + data.is_early = TRUE; + else + data.is_early = FALSE; + /* see if we need to generate SR or RR packets */ if (is_rtcp_time (sess, current_time, &data)) { if (own->received_bye) { @@ -2651,8 +2954,10 @@ rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time, if (data.rtcp) { /* we keep track of the last report time in order to timeout inactive * receivers or senders */ - sess->last_rtcp_send_time = data.current_time; + if (!data.is_early && !data.may_suppress) + sess->last_rtcp_send_time = data.current_time; sess->first_rtcp = FALSE; + sess->next_early_rtcp_time = GST_CLOCK_TIME_NONE; /* add SDES for this source when not already added */ if (!data.has_sdes) @@ -2662,7 +2967,8 @@ rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time, /* check for outdated collisions */ GST_DEBUG ("Timing out collisions"); rtp_source_timeout (sess->source, current_time, - data.interval * RTCP_INTERVAL_COLLISION_TIMEOUT); + data.interval * RTCP_INTERVAL_COLLISION_TIMEOUT, + running_time - sess->rtcp_feedback_retention_window); if (sess->change_ssrc) { GST_DEBUG ("need to change our SSRC (%08x)", own->ssrc); @@ -2682,6 +2988,9 @@ rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time, notify = TRUE; GST_DEBUG ("changed our SSRC to %08x", own->ssrc); } + + sess->allow_early = TRUE; + RTP_SESSION_UNLOCK (sess); if (notify) @@ -2689,20 +2998,175 @@ rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time, /* push out the RTCP packet */ if (data.rtcp) { - /* close the RTCP packet */ - gst_rtcp_buffer_end (data.rtcp); + gboolean do_not_suppress; - GST_DEBUG ("sending packet"); - if (sess->callbacks.send_rtcp) { - UPDATE_AVG (sess->stats.avg_rtcp_packet_size, - GST_BUFFER_SIZE (data.rtcp)); - result = sess->callbacks.send_rtcp (sess, own, data.rtcp, - sess->sent_bye, sess->send_rtcp_user_data); + /* Give the user a change to add its own packet */ + g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SENDING_RTCP], 0, + data.rtcp, data.is_early, &do_not_suppress); + + if (sess->callbacks.send_rtcp && (do_not_suppress || !data.may_suppress)) { + guint packet_size; + + /* close the RTCP packet */ + gst_rtcp_buffer_end (data.rtcp); + + packet_size = GST_BUFFER_SIZE (data.rtcp) + sess->header_len; + + UPDATE_AVG (sess->stats.avg_rtcp_packet_size, packet_size); + GST_DEBUG ("%p, sending RTCP packet, avg size %u, %u", &sess->stats, + sess->stats.avg_rtcp_packet_size, packet_size); + result = + sess->callbacks.send_rtcp (sess, own, data.rtcp, sess->sent_bye, + sess->send_rtcp_user_data); } else { - GST_DEBUG ("freeing packet"); + GST_DEBUG ("freeing packet callback: %p" + " do_not_suppress: %d may_suppress: %d", + sess->callbacks.send_rtcp, do_not_suppress, data.may_suppress); gst_buffer_unref (data.rtcp); } } return result; } + +void +rtp_session_request_early_rtcp (RTPSession * sess, GstClockTime current_time, + GstClockTimeDiff max_delay) +{ + GstClockTime T_dither_max; + + /* Implements the algorithm described in RFC 4585 section 3.5.2 */ + + RTP_SESSION_LOCK (sess); + + /* Check if already requested */ + /* RFC 4585 section 3.5.2 step 2 */ + if (GST_CLOCK_TIME_IS_VALID (sess->next_early_rtcp_time)) + goto dont_send; + + /* Ignore the request a scheduled packet will be in time anyway */ + if (current_time + max_delay > sess->next_rtcp_check_time) + goto dont_send; + + /* RFC 4585 section 3.5.2 step 2b */ + /* If the total sources is <=2, then there is only us and one peer */ + if (sess->total_sources <= 2) { + T_dither_max = 0; + } else { + /* Divide by 2 because l = 0.5 */ + T_dither_max = sess->next_rtcp_check_time - sess->last_rtcp_send_time; + T_dither_max /= 2; + } + + /* RFC 4585 section 3.5.2 step 3 */ + if (current_time + T_dither_max > sess->next_rtcp_check_time) + goto dont_send; + + /* RFC 4585 section 3.5.2 step 4 */ + if (sess->allow_early == FALSE) + goto dont_send; + + if (T_dither_max) { + /* Schedule an early transmission later */ + sess->next_early_rtcp_time = g_random_double () * T_dither_max + + current_time; + } else { + /* If no dithering, schedule it for NOW */ + sess->next_early_rtcp_time = current_time; + } + + RTP_SESSION_UNLOCK (sess); + + /* notify app of need to send packet early + * and therefore of timeout change */ + if (sess->callbacks.reconsider) + sess->callbacks.reconsider (sess, sess->reconsider_user_data); + + return; + +dont_send: + + RTP_SESSION_UNLOCK (sess); + +} + +void +rtp_session_request_key_unit (RTPSession * sess, guint32 ssrc, gboolean fir) +{ + guint i; + + if (fir) + return; + + for (i = 0; i < sess->rtcp_pli_requests->len; i++) + if (ssrc == g_array_index (sess->rtcp_pli_requests, guint32, i)) + return; + + g_array_append_val (sess->rtcp_pli_requests, ssrc); +} + +static gboolean +has_pli_compare_func (gconstpointer a, gconstpointer ignored) +{ + GstRTCPPacket packet; + + packet.buffer = (GstBuffer *) a; + packet.offset = 0; + + if (gst_rtcp_packet_get_type (&packet) == GST_RTCP_TYPE_PSFB && + gst_rtcp_packet_fb_get_type (&packet) == GST_RTCP_PSFB_TYPE_PLI) + return TRUE; + else + return FALSE; +} + +static gboolean +rtp_session_on_sending_rtcp (RTPSession * sess, GstBuffer * buffer, + gboolean early) +{ + gboolean ret = FALSE; + + RTP_SESSION_LOCK (sess); + + while (sess->rtcp_pli_requests->len) { + GstRTCPPacket rtcppacket; + guint media_ssrc = g_array_index (sess->rtcp_pli_requests, guint32, 0); + RTPSource *media_src = g_hash_table_lookup (sess->ssrcs[sess->mask_idx], + GUINT_TO_POINTER (media_ssrc)); + + if (media_src && !rtp_source_has_retained (media_src, + has_pli_compare_func, NULL)) { + if (gst_rtcp_buffer_add_packet (buffer, GST_RTCP_TYPE_PSFB, &rtcppacket)) { + gst_rtcp_packet_fb_set_type (&rtcppacket, GST_RTCP_PSFB_TYPE_PLI); + gst_rtcp_packet_fb_set_sender_ssrc (&rtcppacket, + rtp_source_get_ssrc (sess->source)); + gst_rtcp_packet_fb_set_media_ssrc (&rtcppacket, media_ssrc); + ret = TRUE; + } else { + /* Break because the packet is full, will put next request in a + * further packet + */ + break; + } + } + + g_array_remove_index (sess->rtcp_pli_requests, 0); + } + + RTP_SESSION_UNLOCK (sess); + + return ret; +} + +static void +rtp_session_send_rtcp (RTPSession * sess, GstClockTimeDiff max_delay) +{ + GstClockTime now; + + if (!sess->callbacks.send_rtcp) + return; + + now = sess->callbacks.request_time (sess, sess->request_time_user_data); + + rtp_session_request_early_rtcp (sess, now, max_delay); +} diff --git a/gst/rtpmanager/rtpsession.h b/gst/rtpmanager/rtpsession.h index 3fc9874675..6dee1cbe28 100644 --- a/gst/rtpmanager/rtpsession.h +++ b/gst/rtpmanager/rtpsession.h @@ -120,6 +120,30 @@ typedef gint (*RTPSessionClockRate) (RTPSession *sess, guint8 payload, gpointer */ typedef void (*RTPSessionReconsider) (RTPSession *sess, gpointer user_data); +/** + * RTPSessionRequestKeyUnit: + * @sess: an #RTPSession + * @all_headers: %TRUE if "all-headers" property should be set on the key unit + * request + * @user_data: user data specified when registering +* + * Asks the encoder to produce a key unit as soon as possibly within the + * bandwidth constraints + */ +typedef void (*RTPSessionRequestKeyUnit) (RTPSession *sess, + gboolean all_headers, gpointer user_data); + +/** + * RTPSessionRequestTime: + * @sess: an #RTPSession + * @user_data: user data specified when registering + * + * This callback will be called when @sess needs the current time. The time + * should be returned as a #GstClockTime + */ +typedef GstClockTime (*RTPSessionRequestTime) (RTPSession *sess, + gpointer user_data); + /** * RTPSessionCallbacks: * @RTPSessionProcessRTP: callback to process RTP packets @@ -127,6 +151,7 @@ typedef void (*RTPSessionReconsider) (RTPSession *sess, gpointer user_data); * @RTPSessionSendRTCP: callback for sending RTCP packets * @RTPSessionSyncRTCP: callback for handling SR packets * @RTPSessionReconsider: callback for reconsidering the timeout + * @RTPSessionRequestKeyUnit: callback for requesting a new key unit * * These callbacks can be installed on the session manager to get notification * when RTP and RTCP packets are ready for further processing. These callbacks @@ -139,6 +164,8 @@ typedef struct { RTPSessionSendRTCP send_rtcp; RTPSessionClockRate clock_rate; RTPSessionReconsider reconsider; + RTPSessionRequestKeyUnit request_key_unit; + RTPSessionRequestTime request_time; } RTPSessionCallbacks; /** @@ -183,6 +210,9 @@ struct _RTPSession { GstClockTime next_rtcp_check_time; GstClockTime last_rtcp_send_time; gboolean first_rtcp; + gboolean allow_early; + + GstClockTime next_early_rtcp_time; gchar *bye_reason; gboolean sent_bye; @@ -194,11 +224,17 @@ struct _RTPSession { gpointer sync_rtcp_user_data; gpointer clock_rate_user_data; gpointer reconsider_user_data; + gpointer request_key_unit_user_data; + gpointer request_time_user_data; RTPSessionStats stats; gboolean change_ssrc; gboolean favor_new; + GstClockTime rtcp_feedback_retention_window; + + GArray *rtcp_pli_requests; + GstClockTime last_keyframe_request; }; /** @@ -224,6 +260,11 @@ struct _RTPSessionClass { void (*on_bye_timeout) (RTPSession *sess, RTPSource *source); void (*on_timeout) (RTPSession *sess, RTPSource *source); void (*on_sender_timeout) (RTPSession *sess, RTPSource *source); + gboolean (*on_sending_rtcp) (RTPSession *sess, GstBuffer *buffer, + gboolean early); + void (*on_feedback_rtcp) (RTPSession *sess, guint type, guint fbtype, + guint sender_ssrc, guint media_ssrc, GstBuffer *fci); + void (*send_rtcp) (RTPSession *sess, GstClockTimeDiff max_delay); }; GType rtp_session_get_type (void); @@ -251,6 +292,10 @@ void rtp_session_set_clock_rate_callback (RTPSession * sess, void rtp_session_set_reconsider_callback (RTPSession * sess, RTPSessionReconsider callback, gpointer user_data); +void rtp_session_set_request_time_callback (RTPSession * sess, + RTPSessionRequestTime callback, + gpointer user_data); + void rtp_session_set_bandwidth (RTPSession *sess, gdouble bandwidth); gdouble rtp_session_get_bandwidth (RTPSession *sess); void rtp_session_set_rtcp_fraction (RTPSession *sess, gdouble fraction); @@ -281,7 +326,8 @@ GstFlowReturn rtp_session_process_rtp (RTPSession *sess, GstBuffer GstClockTime current_time, GstClockTime running_time); GstFlowReturn rtp_session_process_rtcp (RTPSession *sess, GstBuffer *buffer, - GstClockTime current_time); + GstClockTime current_time, + guint64 ntpnstime); /* processing packets for sending */ GstFlowReturn rtp_session_send_rtp (RTPSession *sess, gpointer data, gboolean is_list, @@ -296,4 +342,13 @@ GstClockTime rtp_session_next_timeout (RTPSession *sess, GstClockTi GstFlowReturn rtp_session_on_timeout (RTPSession *sess, GstClockTime current_time, guint64 ntpnstime, GstClockTime running_time); +/* request the transmittion of an early RTCP packet */ +void rtp_session_request_early_rtcp (RTPSession * sess, GstClockTime current_time, + GstClockTimeDiff max_delay); + +/* Notify session of a request for a new key unit */ +void rtp_session_request_key_unit (RTPSession * sess, + guint32 ssrc, + gboolean fir); + #endif /* __RTP_SESSION_H__ */ diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c index e0432b76d5..fb9e039553 100644 --- a/gst/rtpmanager/rtpsource.c +++ b/gst/rtpmanager/rtpsource.c @@ -123,6 +123,76 @@ rtp_source_class_init (RTPSourceClass * klass) * The statistics of the source. This property returns a GstStructure with * name application/x-rtp-source-stats with the following fields: * + * "ssrc" G_TYPE_UINT The SSRC of this source + * "internal" G_TYPE_BOOLEAN If this source is the source of the session + * "validated" G_TYPE_BOOLEAN If the source is validated + * "received-bye" G_TYPE_BOOLEAN If we received a BYE from this source + * "is-csrc" G_TYPE_BOOLEAN If this source was found as CSRC + * "is-sender" G_TYPE_BOOLEAN If this source is a sender + * "seqnum-base" G_TYPE_INT first seqnum if known + * "clock-rate" G_TYPE_INT the clock rate of the media + * + * The following two fields are only present when known. + * + * "rtp-from" G_TYPE_STRING where we received the last RTP packet from + * "rtcp-from" G_TYPE_STRING where we received the last RTCP packet from + * + * The following fields make sense for internal sources and will only increase + * when "is-sender" is TRUE: + * + * "octets-sent" G_TYPE_UINT64 number of bytes we sent + * "packets-sent" G_TYPE_UINT64 number of packets we sent + * + * The following fields make sense for non-internal sources and will only + * increase when "is-sender" is TRUE. + * + * "octets-received" G_TYPE_UINT64 total number of bytes received + * "packets-received" G_TYPE_UINT64 total number of packets received + * + * Following fields are updated when "is-sender" is TRUE. + * + * "bitrate" G_TYPE_UINT64 bitrate in bits per second + * "jitter" G_TYPE_UINT estimated jitter + * "packets-lost" G_TYPE_INT estimated amount of packets lost + * + * The last SR report this source sent. This only updates when "is-sender" is + * TRUE. + * + * "have-sr" G_TYPE_BOOLEAN the source has sent SR + * "sr-ntptime" G_TYPE_UINT64 ntptime of SR + * "sr-rtptime" G_TYPE_UINT rtptime of SR + * "sr-octet-count" G_TYPE_UINT the number of bytes in the SR + * "sr-packet-count" G_TYPE_UINT the number of packets in the SR + * + * The following fields are only present for non-internal sources and + * represent the content of the last RB packet that was sent to this source. + * These values are only updated when the source is sending. + * + * "sent-rb" G_TYPE_BOOLEAN we have sent an RB + * "sent-rb-fractionlost" G_TYPE_UINT calculated lost fraction + * "sent-rb-packetslost" G_TYPE_INT lost packets + * "sent-rb-exthighestseq" G_TYPE_UINT last seen seqnum + * "sent-rb-jitter" G_TYPE_UINT jitter + * "sent-rb-lsr" G_TYPE_UINT last SR time + * "sent-rb-dlsr" G_TYPE_UINT delay since last SR + * + * The following fields are only present for non-internal sources and + * represents the last RB that this source sent. This is only updated + * when the source is receiving data and sending RB blocks. + * + * "have-rb" G_TYPE_BOOLEAN the source has sent RB + * "rb-fractionlost" G_TYPE_UINT lost fraction + * "rb-packetslost" G_TYPE_INT lost packets + * "rb-exthighestseq" G_TYPE_UINT highest received seqnum + * "rb-jitter" G_TYPE_UINT reception jitter + * "rb-lsr" G_TYPE_UINT last SR time + * "rb-dlsr" G_TYPE_UINT delay since last SR + * + * The round trip of this source. This is calculated from the last RB + * values and the recption time of the last RB packet. Only present for + * non-internal sources. + * + * "rb-round-trip" G_TYPE_UINT the round trip time in nanoseconds */ g_object_class_install_property (gobject_class, PROP_STATS, g_param_spec_boxed ("stats", "Stats", @@ -168,6 +238,8 @@ rtp_source_init (RTPSource * src) src->seqnum_base = -1; src->last_rtptime = -1; + src->retained_feedback = g_queue_new (); + rtp_source_reset (src); } @@ -192,6 +264,10 @@ rtp_source_finalize (GObject * object) g_list_foreach (src->conflicting_addresses, (GFunc) g_free, NULL); g_list_free (src->conflicting_addresses); + while ((buffer = g_queue_pop_head (src->retained_feedback))) + gst_buffer_unref (buffer); + g_queue_free (src->retained_feedback); + G_OBJECT_CLASS (rtp_source_parent_class)->finalize (object); } @@ -202,6 +278,21 @@ rtp_source_create_stats (RTPSource * src) gboolean is_sender = src->is_sender; gboolean internal = src->internal; gchar address_str[GST_NETADDRESS_MAX_LEN]; + gboolean have_rb; + guint8 fractionlost = 0; + gint32 packetslost = 0; + guint32 exthighestseq = 0; + guint32 jitter = 0; + guint32 lsr = 0; + guint32 dlsr = 0; + guint32 round_trip = 0; + gboolean have_sr; + GstClockTime time = 0; + guint64 ntptime = 0; + guint32 rtptime = 0; + guint32 packet_count = 0; + guint32 octet_count = 0; + /* common data for all types of sources */ s = gst_structure_new ("application/x-rtp-source-stats", @@ -226,57 +317,39 @@ rtp_source_create_stats (RTPSource * src) gst_structure_set (s, "rtcp-from", G_TYPE_STRING, address_str, NULL); } - if (internal) { - /* our internal source */ + gst_structure_set (s, + "octets-sent", G_TYPE_UINT64, src->stats.octets_sent, + "packets-sent", G_TYPE_UINT64, src->stats.packets_sent, + "octets-received", G_TYPE_UINT64, src->stats.octets_received, + "packets-received", G_TYPE_UINT64, src->stats.packets_received, + "bitrate", G_TYPE_UINT64, src->bitrate, + "packets-lost", G_TYPE_INT, + (gint) rtp_stats_get_packets_lost (&src->stats), "jitter", G_TYPE_UINT, + (guint) (src->stats.jitter >> 4), NULL); - /* report accumulated send statistics, other sources will have a RB with - * info on reception. */ + /* get the last SR. */ + have_sr = rtp_source_get_last_sr (src, &time, &ntptime, &rtptime, + &packet_count, &octet_count); + gst_structure_set (s, + "have-sr", G_TYPE_BOOLEAN, have_sr, + "sr-ntptime", G_TYPE_UINT64, ntptime, + "sr-rtptime", G_TYPE_UINT, (guint) rtptime, + "sr-octet-count", G_TYPE_UINT, (guint) octet_count, + "sr-packet-count", G_TYPE_UINT, (guint) packet_count, NULL); + + if (!internal) { + /* get the last RB we sent */ gst_structure_set (s, - "octets-sent", G_TYPE_UINT64, src->stats.octets_sent, - "packets-sent", G_TYPE_UINT64, src->stats.packets_sent, NULL); + "sent-rb", G_TYPE_BOOLEAN, src->last_rr.is_valid, + "sent-rb-fractionlost", G_TYPE_UINT, (guint) src->last_rr.fractionlost, + "sent-rb-packetslost", G_TYPE_INT, (gint) src->last_rr.packetslost, + "sent-rb-exthighestseq", G_TYPE_UINT, + (guint) src->last_rr.exthighestseq, "sent-rb-jitter", G_TYPE_UINT, + (guint) src->last_rr.jitter, "sent-rb-lsr", G_TYPE_UINT, + (guint) src->last_rr.lsr, "sent-rb-dlsr", G_TYPE_UINT, + (guint) src->last_rr.dlsr, NULL); - if (is_sender) - gst_structure_set (s, "bitrate", G_TYPE_UINT64, src->bitrate, NULL); - - } else { - /* other sources */ - gboolean have_rb; - guint8 fractionlost = 0; - gint32 packetslost = 0; - guint32 exthighestseq = 0; - guint32 jitter = 0; - guint32 lsr = 0; - guint32 dlsr = 0; - guint32 round_trip = 0; - - gst_structure_set (s, - "octets-received", G_TYPE_UINT64, src->stats.octets_received, - "packets-received", G_TYPE_UINT64, src->stats.packets_received, - "bitrate", G_TYPE_UINT64, src->bitrate, - "packets-lost", G_TYPE_INT, - (gint) rtp_stats_get_packets_lost (&src->stats), "jitter", G_TYPE_UINT, - (guint) (src->stats.jitter >> 4), NULL); - - if (is_sender) { - gboolean have_sr; - GstClockTime time = 0; - guint64 ntptime = 0; - guint32 rtptime = 0; - guint32 packet_count = 0; - guint32 octet_count = 0; - - /* this source is sending to us, get the last SR. */ - have_sr = rtp_source_get_last_sr (src, &time, &ntptime, &rtptime, - &packet_count, &octet_count); - gst_structure_set (s, - "have-sr", G_TYPE_BOOLEAN, have_sr, - "sr-ntptime", G_TYPE_UINT64, ntptime, - "sr-rtptime", G_TYPE_UINT, (guint) rtptime, - "sr-octet-count", G_TYPE_UINT, (guint) octet_count, - "sr-packet-count", G_TYPE_UINT, (guint) packet_count, NULL); - } - /* we might be sending to this SSRC so we report about how it is - * receiving our data */ + /* get the last RB */ have_rb = rtp_source_get_last_rb (src, &fractionlost, &packetslost, &exthighestseq, &jitter, &lsr, &dlsr, &round_trip); @@ -1277,7 +1350,7 @@ rtp_source_process_sr (RTPSource * src, GstClockTime time, guint64 ntptime, /** * rtp_source_process_rb: * @src: an #RTPSource - * @time: the current time in nanoseconds since 1970 + * @ntpnstime: the current time in nanoseconds since 1970 * @fractionlost: fraction lost since last SR/RR * @packetslost: the cumululative number of packets lost * @exthighestseq: the extended last sequence number received @@ -1288,13 +1361,14 @@ rtp_source_process_sr (RTPSource * src, GstClockTime time, guint64 ntptime, * Update the report block in @src. */ void -rtp_source_process_rb (RTPSource * src, GstClockTime time, guint8 fractionlost, - gint32 packetslost, guint32 exthighestseq, guint32 jitter, guint32 lsr, - guint32 dlsr) +rtp_source_process_rb (RTPSource * src, guint64 ntpnstime, + guint8 fractionlost, gint32 packetslost, guint32 exthighestseq, + guint32 jitter, guint32 lsr, guint32 dlsr) { RTPReceiverReport *curr; gint curridx; guint32 ntp, A; + guint64 f_ntp; g_return_if_fail (RTP_IS_SOURCE (src)); @@ -1315,8 +1389,11 @@ rtp_source_process_rb (RTPSource * src, GstClockTime time, guint8 fractionlost, curr->lsr = lsr; curr->dlsr = dlsr; + /* convert the NTP time in nanoseconds to 32.32 fixed point */ + f_ntp = gst_util_uint64_scale (ntpnstime, (1LL << 32), GST_SECOND); /* calculate round trip, round the time up */ - ntp = ((gst_rtcp_unix_to_ntp (time) + 0xffff) >> 16) & 0xffffffff; + ntp = ((f_ntp + 0xffff) >> 16) & 0xffffffff; + A = dlsr + lsr; if (A > 0 && ntp > A) A = ntp - A; @@ -1652,14 +1729,18 @@ rtp_source_add_conflicting_address (RTPSource * src, * @src: The #RTPSource * @current_time: The current time * @collision_timeout: The amount of time after which a collision is timed out + * @feedback_retention_window: The running time before which retained feedback + * packets have to be discarded * * This is processed on each RTCP interval. It times out old collisions. + * It also times out old retained feedback packets */ void rtp_source_timeout (RTPSource * src, GstClockTime current_time, - GstClockTime collision_timeout) + GstClockTime collision_timeout, GstClockTime feedback_retention_window) { GList *item; + GstRTCPPacket *pkt; item = g_list_first (src->conflicting_addresses); while (item) { @@ -1677,4 +1758,41 @@ rtp_source_timeout (RTPSource * src, GstClockTime current_time, } item = next_item; } + + /* Time out AVPF packets that are older than the desired length */ + while ((pkt = g_queue_peek_tail (src->retained_feedback)) && + GST_BUFFER_TIMESTAMP (pkt) < feedback_retention_window) + gst_buffer_unref (g_queue_pop_tail (src->retained_feedback)); +} + +static gint +compare_buffers (gconstpointer a, gconstpointer b, gpointer user_data) +{ + const GstBuffer *bufa = a; + const GstBuffer *bufb = b; + + return GST_BUFFER_TIMESTAMP (bufa) - GST_BUFFER_TIMESTAMP (bufb); +} + +void +rtp_source_retain_rtcp_packet (RTPSource * src, GstRTCPPacket * packet, + GstClockTime running_time) +{ + GstBuffer *buffer; + + buffer = gst_buffer_create_sub (packet->buffer, packet->offset, + (gst_rtcp_packet_get_length (packet) + 1) * 4); + + GST_BUFFER_TIMESTAMP (buffer) = running_time; + + g_queue_insert_sorted (src->retained_feedback, buffer, compare_buffers, NULL); +} + +gboolean +rtp_source_has_retained (RTPSource * src, GCompareFunc func, gconstpointer data) +{ + if (g_queue_find_custom (src->retained_feedback, data, func)) + return TRUE; + else + return FALSE; } diff --git a/gst/rtpmanager/rtpsource.h b/gst/rtpmanager/rtpsource.h index 54a86ac47c..6db0a6a678 100644 --- a/gst/rtpmanager/rtpsource.h +++ b/gst/rtpmanager/rtpsource.h @@ -167,8 +167,11 @@ struct _RTPSource { gpointer user_data; RTPSourceStats stats; + RTPReceiverReport last_rr; GList *conflicting_addresses; + + GQueue *retained_feedback; }; struct _RTPSourceClass { @@ -217,7 +220,7 @@ GstFlowReturn rtp_source_send_rtp (RTPSource *src, gpointer data, g void rtp_source_process_bye (RTPSource *src, const gchar *reason); void rtp_source_process_sr (RTPSource *src, GstClockTime time, guint64 ntptime, guint32 rtptime, guint32 packet_count, guint32 octet_count); -void rtp_source_process_rb (RTPSource *src, GstClockTime time, guint8 fractionlost, +void rtp_source_process_rb (RTPSource *src, guint64 ntpnstime, guint8 fractionlost, gint32 packetslost, guint32 exthighestseq, guint32 jitter, guint32 lsr, guint32 dlsr); @@ -247,7 +250,16 @@ void rtp_source_add_conflicting_address (RTPSource * src, void rtp_source_timeout (RTPSource * src, GstClockTime current_time, - GstClockTime collision_timeout); + GstClockTime collision_timeout, + GstClockTime feedback_retention_window); + +void rtp_source_retain_rtcp_packet (RTPSource * src, + GstRTCPPacket *pkt, + GstClockTime running_time); + +gboolean rtp_source_has_retained (RTPSource * src, + GCompareFunc func, + gconstpointer data); #endif /* __RTP_SOURCE_H__ */ diff --git a/gst/rtpmanager/rtpstats.c b/gst/rtpmanager/rtpstats.c index a6de7d880b..8fe1d1fce6 100644 --- a/gst/rtpmanager/rtpstats.c +++ b/gst/rtpmanager/rtpstats.c @@ -166,7 +166,7 @@ rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send, if (rtcp_bw <= 0.00001) return GST_CLOCK_TIME_NONE; - avg_rtcp_size = stats->avg_rtcp_packet_size / 16.0; + avg_rtcp_size = stats->avg_rtcp_packet_size; /* * The effective number of sites times the average packet size is * the total number of octets sent when each site sends a report. @@ -176,6 +176,7 @@ rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send, * time interval we send one report so this time is also our * average time between reports. */ + GST_DEBUG ("avg size %f, n %f, rtcp_bw %f", avg_rtcp_size, n, rtcp_bw); interval = avg_rtcp_size * n / rtcp_bw; if (interval < rtcp_min_time) interval = rtcp_min_time; @@ -245,7 +246,7 @@ rtp_stats_calculate_bye_interval (RTPSessionStats * stats) if (rtcp_bw <= 0.0001) return GST_CLOCK_TIME_NONE; - avg_rtcp_size = stats->avg_rtcp_packet_size / 16.0; + avg_rtcp_size = stats->avg_rtcp_packet_size; /* * The effective number of sites times the average packet size is * the total number of octets sent when each site sends a report. @@ -274,7 +275,7 @@ rtp_stats_calculate_bye_interval (RTPSessionStats * stats) * Returns: total RTP packets lost. */ gint64 -rtp_stats_get_packets_lost (const RTPSourceStats *stats) +rtp_stats_get_packets_lost (const RTPSourceStats * stats) { gint64 lost; guint64 extended_max, expected; @@ -284,4 +285,10 @@ rtp_stats_get_packets_lost (const RTPSourceStats *stats) lost = expected - stats->packets_received; return lost; -} \ No newline at end of file +} + +void +rtp_stats_set_min_interval (RTPSessionStats * stats, gdouble min_interval) +{ + stats->min_interval = min_interval; +} diff --git a/gst/rtpmanager/rtpstats.h b/gst/rtpmanager/rtpstats.h index d657cdfed0..4560595edb 100644 --- a/gst/rtpmanager/rtpstats.h +++ b/gst/rtpmanager/rtpstats.h @@ -58,6 +58,7 @@ typedef struct { * RTPArrivalStats: * @current_time: current time according to the system clock * @running_time: arrival time of a packet as buffer running_time + * @ntpnstime: arrival time of a packet NTP time in nanoseconds * @have_address: if the @address field contains a valid address * @address: address of the sender of the packet * @bytes: bytes of the packet including lowlevel overhead @@ -68,6 +69,7 @@ typedef struct { typedef struct { GstClockTime current_time; GstClockTime running_time; + guint64 ntpnstime; gboolean have_address; GstNetAddress address; guint bytes; @@ -195,4 +197,7 @@ GstClockTime rtp_stats_calculate_rtcp_interval (RTPSessionStats *stats, gbo GstClockTime rtp_stats_add_rtcp_jitter (RTPSessionStats *stats, GstClockTime interval); GstClockTime rtp_stats_calculate_bye_interval (RTPSessionStats *stats); gint64 rtp_stats_get_packets_lost (const RTPSourceStats *stats); + +void rtp_stats_set_min_interval (RTPSessionStats *stats, + gdouble min_interval); #endif /* __RTP_STATS_H__ */ diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index 0382fc90ea..b78a8fa622 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -56,15 +56,14 @@ * For each stream listed in the SDP a new rtp_stream%d pad will be created * with caps derived from the SDP media description. This is a caps of mime type * "application/x-rtp" that can be connected to any available RTP depayloader - * element. + * element. * * rtspsrc will internally instantiate an RTP session manager element * that will handle the RTCP messages to and from the server, jitter removal, - * packet reordering along with providing a clock for the pipeline. - * This feature is currently fully implemented with the gstrtpbin in the - * gst-plugins-bad module. + * packet reordering along with providing a clock for the pipeline. + * This feature is implemented using the gstrtpbin element. * - * rtspsrc acts like a live source and will therefore only generate data in the + * rtspsrc acts like a live source and will therefore only generate data in the * PLAYING state. * * @@ -129,15 +128,24 @@ enum LAST_SIGNAL }; +enum _GstRtspSrcBufferMode +{ + BUFFER_MODE_NONE, + BUFFER_MODE_SLAVE, + BUFFER_MODE_BUFFER, + BUFFER_MODE_AUTO +}; + #define GST_TYPE_RTSP_SRC_BUFFER_MODE (gst_rtsp_src_buffer_mode_get_type()) static GType gst_rtsp_src_buffer_mode_get_type (void) { static GType buffer_mode_type = 0; static const GEnumValue buffer_modes[] = { - {0, "Only use RTP timestamps", "none"}, - {1, "Slave receiver to sender clock", "slave"}, - {2, "Do low/high watermark buffering", "buffer"}, + {BUFFER_MODE_NONE, "Only use RTP timestamps", "none"}, + {BUFFER_MODE_SLAVE, "Slave receiver to sender clock", "slave"}, + {BUFFER_MODE_BUFFER, "Do low/high watermark buffering", "buffer"}, + {BUFFER_MODE_AUTO, "Choose mode depending on stream live", "auto"}, {0, NULL, NULL}, }; @@ -153,7 +161,7 @@ gst_rtsp_src_buffer_mode_get_type (void) #define DEFAULT_DEBUG FALSE #define DEFAULT_RETRY 20 #define DEFAULT_TIMEOUT 5000000 -#define DEFAULT_UDP_BUFFER_SIZE 0 +#define DEFAULT_UDP_BUFFER_SIZE 0x80000 #define DEFAULT_TCP_TIMEOUT 20000000 #define DEFAULT_LATENCY_MS 2000 #define DEFAULT_CONNECTION_SPEED 0 @@ -163,7 +171,7 @@ gst_rtsp_src_buffer_mode_get_type (void) #define DEFAULT_RTP_BLOCKSIZE 0 #define DEFAULT_USER_ID NULL #define DEFAULT_USER_PW NULL -#define DEFAULT_BUFFER_MODE 1 +#define DEFAULT_BUFFER_MODE BUFFER_MODE_AUTO #define DEFAULT_PORT_RANGE NULL enum @@ -252,7 +260,6 @@ static gboolean gst_rtspsrc_stream_push_event (GstRTSPSrc * src, GstRTSPStream * stream, GstEvent * event, gboolean source); static gboolean gst_rtspsrc_push_event (GstRTSPSrc * src, GstEvent * event, gboolean source); -static gchar *gst_rtspsrc_dup_printf (const gchar * format, ...); /* commands we send to out loop to notify it of events */ #define CMD_WAIT 0 @@ -521,6 +528,7 @@ gst_rtspsrc_finalize (GObject * object) gst_rtsp_ext_list_free (rtspsrc->extensions); g_free (rtspsrc->conninfo.location); gst_rtsp_url_free (rtspsrc->conninfo.url); + g_free (rtspsrc->conninfo.url_str); g_free (rtspsrc->user_id); g_free (rtspsrc->user_pw); @@ -1118,6 +1126,10 @@ gst_rtspsrc_stream_free (GstRTSPSrc * src, GstRTSPStream * stream) gst_object_unref (stream->rtcppad); stream->rtcppad = NULL; } + if (stream->session) { + g_object_unref (stream->session); + stream->session = NULL; + } g_free (stream); } @@ -1135,14 +1147,14 @@ gst_rtspsrc_cleanup (GstRTSPSrc * src) } g_list_free (src->streams); src->streams = NULL; - if (src->session) { - if (src->session_sig_id) { - g_signal_handler_disconnect (src->session, src->session_sig_id); - src->session_sig_id = 0; + if (src->manager) { + if (src->manager_sig_id) { + g_signal_handler_disconnect (src->manager, src->manager_sig_id); + src->manager_sig_id = 0; } - gst_element_set_state (src->session, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (src), src->session); - src->session = NULL; + gst_element_set_state (src->manager, GST_STATE_NULL); + gst_bin_remove (GST_BIN_CAST (src), src->manager); + src->manager = NULL; } src->numstreams = 0; if (src->props) @@ -1297,7 +1309,7 @@ gst_rtspsrc_sdp_attributes_to_caps (GArray * attributes, GstCaps * caps) /* * Mapping of caps to and from SDP fields: * - * m= RTP/AVP + * m= RTP/AVP * a=rtpmap: /[/] * a=fmtp: [=];... */ @@ -1483,6 +1495,10 @@ again: goto no_udp_protocol; g_object_set (G_OBJECT (udpsrc0), "port", tmp_rtp, "reuse", FALSE, NULL); + if (src->udp_buffer_size != 0) + g_object_set (G_OBJECT (udpsrc0), "buffer-size", src->udp_buffer_size, + NULL); + ret = gst_element_set_state (udpsrc0, GST_STATE_PAUSED); if (ret == GST_STATE_CHANGE_FAILURE) { if (tmp_rtp != 0) { @@ -1660,8 +1676,8 @@ gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush) if (base_time != -1) gst_element_set_base_time (GST_ELEMENT_CAST (src), base_time); /* to manage jitterbuffer buffer mode */ - if (src->session) - gst_element_set_base_time (GST_ELEMENT_CAST (src->session), base_time); + if (src->manager) + gst_element_set_base_time (GST_ELEMENT_CAST (src->manager), base_time); } static GstRTSPResult @@ -2020,6 +2036,11 @@ gst_rtspsrc_handle_src_query (GstPad * pad, GstQuery * query) gboolean seekable = src->cur_protocols != GST_RTSP_LOWER_TRANS_UDP_MCAST; + /* seeking without duration is unlikely */ + seekable = seekable && src->seekable && src->segment.duration && + GST_CLOCK_TIME_IS_VALID (src->segment.duration); + + /* FIXME ?? should we have 0 and segment.duration here; see demuxers */ gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, src->segment.start, src->segment.stop); res = TRUE; @@ -2121,7 +2142,7 @@ was_ok: /* this callback is called when the session manager generated a new src pad with * payloaded RTP packets. We simply ghost the pad here. */ static void -new_session_pad (GstElement * session, GstPad * pad, GstRTSPSrc * src) +new_manager_pad (GstElement * manager, GstPad * pad, GstRTSPSrc * src) { gchar *name; GstPadTemplate *template; @@ -2130,7 +2151,7 @@ new_session_pad (GstElement * session, GstPad * pad, GstRTSPSrc * src) GstRTSPStream *stream; gboolean all_added; - GST_DEBUG_OBJECT (src, "got new session pad %" GST_PTR_FORMAT, pad); + GST_DEBUG_OBJECT (src, "got new manager pad %" GST_PTR_FORMAT, pad); GST_RTSP_STATE_LOCK (src); /* find stream */ @@ -2193,7 +2214,7 @@ unknown_stream: } static GstCaps * -request_pt_map (GstElement * sess, guint session, guint pt, GstRTSPSrc * src) +request_pt_map (GstElement * manager, guint session, guint pt, GstRTSPSrc * src) { GstRTSPStream *stream; GstCaps *caps; @@ -2221,16 +2242,9 @@ unknown_stream: } static void -gst_rtspsrc_do_stream_eos (GstRTSPSrc * src, guint session) +gst_rtspsrc_do_stream_eos (GstRTSPSrc * src, GstRTSPStream * stream) { - GstRTSPStream *stream; - - GST_DEBUG_OBJECT (src, "setting stream for session %u to EOS", session); - - /* get stream for session */ - stream = find_stream (src, &session, (gpointer) find_stream_by_id); - if (!stream) - goto unknown_stream; + GST_DEBUG_OBJECT (src, "setting stream for session %u to EOS", stream->id); if (stream->eos) goto was_eos; @@ -2240,43 +2254,52 @@ gst_rtspsrc_do_stream_eos (GstRTSPSrc * src, guint session) return; /* ERRORS */ -unknown_stream: - { - GST_DEBUG_OBJECT (src, "unknown stream for session %u", session); - return; - } was_eos: { - GST_DEBUG_OBJECT (src, "stream for session %u was already EOS", session); + GST_DEBUG_OBJECT (src, "stream for session %u was already EOS", stream->id); return; } } static void -on_bye_ssrc (GstElement * manager, guint session, guint32 ssrc, - GstRTSPSrc * src) +on_bye_ssrc (GObject * session, GObject * source, GstRTSPStream * stream) { - GST_DEBUG_OBJECT (src, "SSRC %08x in session %u received BYE", ssrc, session); + GstRTSPSrc *src = stream->parent; - gst_rtspsrc_do_stream_eos (src, session); + GST_DEBUG_OBJECT (src, "source in session %u received BYE", stream->id); + + gst_rtspsrc_do_stream_eos (src, stream); } static void -on_timeout (GstElement * manager, guint session, guint32 ssrc, GstRTSPSrc * src) +on_timeout (GObject * session, GObject * source, GstRTSPStream * stream) { - GST_DEBUG_OBJECT (src, "SSRC %08x in session %u timed out", ssrc, session); + GstRTSPSrc *src = stream->parent; - gst_rtspsrc_do_stream_eos (src, session); + GST_DEBUG_OBJECT (src, "source in session %u timed out", stream->id); + + gst_rtspsrc_do_stream_eos (src, stream); } static void -on_npt_stop (GstElement * manager, guint session, guint32 ssrc, - GstRTSPSrc * src) +on_npt_stop (GstElement * rtpbin, guint session, guint ssrc, GstRTSPSrc * src) { - GST_DEBUG_OBJECT (src, "SSRC %08x in session %u reached the NPT stop", ssrc, - session); + GstRTSPStream *stream; - gst_rtspsrc_do_stream_eos (src, session); + GST_DEBUG_OBJECT (src, "source in session %u reached NPT stop", session); + + /* get stream for session */ + stream = find_stream (src, &session, (gpointer) find_stream_by_id); + if (stream) { + gst_rtspsrc_do_stream_eos (src, stream); + } +} + +static void +on_ssrc_active (GObject * session, GObject * source, GstRTSPStream * stream) +{ + GST_DEBUG_OBJECT (stream->parent, "source in session %u is active", + stream->id); } /* try to get and configure a manager */ @@ -2296,11 +2319,11 @@ gst_rtspsrc_stream_configure_manager (GstRTSPSrc * src, GstRTSPStream * stream, GST_DEBUG_OBJECT (src, "using manager %s", manager); /* configure the manager */ - if (src->session == NULL) { + if (src->manager == NULL) { GObjectClass *klass; GstState target; - if (!(src->session = gst_element_factory_make (manager, NULL))) { + if (!(src->manager = gst_element_factory_make (manager, NULL))) { /* fallback */ if (gst_rtsp_transport_get_manager (transport->trans, &manager, 1) < 0) goto no_manager; @@ -2308,70 +2331,93 @@ gst_rtspsrc_stream_configure_manager (GstRTSPSrc * src, GstRTSPStream * stream, if (!manager) goto use_no_manager; - if (!(src->session = gst_element_factory_make (manager, NULL))) + if (!(src->manager = gst_element_factory_make (manager, NULL))) goto manager_failed; } /* we manage this element */ - gst_bin_add (GST_BIN_CAST (src), src->session); + gst_bin_add (GST_BIN_CAST (src), src->manager); GST_OBJECT_LOCK (src); target = GST_STATE_TARGET (src); GST_OBJECT_UNLOCK (src); - ret = gst_element_set_state (src->session, target); + ret = gst_element_set_state (src->manager, target); if (ret == GST_STATE_CHANGE_FAILURE) - goto start_session_failure; + goto start_manager_failure; - g_object_set (src->session, "latency", src->latency, NULL); + g_object_set (src->manager, "latency", src->latency, NULL); - klass = G_OBJECT_GET_CLASS (G_OBJECT (src->session)); - if (g_object_class_find_property (klass, "buffer-mode")) - g_object_set (src->session, "buffer-mode", src->buffer_mode, NULL); + klass = G_OBJECT_GET_CLASS (G_OBJECT (src->manager)); + if (g_object_class_find_property (klass, "buffer-mode")) { + if (src->buffer_mode != BUFFER_MODE_AUTO) { + g_object_set (src->manager, "buffer-mode", src->buffer_mode, NULL); + } else { + gboolean need_slave; + GstStructure *s; + const gchar *encoding; + + /* buffer mode pauses are handled by adding offsets to buffer times, + * but some depayloaders may have a hard time syncing output times + * with such input times, e.g. container ones, most notably ASF */ + /* TODO alternatives are having an event that indicates these shifts, + * or having rtsp extensions provide suggestion on buffer mode */ + need_slave = stream->container; + if (stream->caps && (s = gst_caps_get_structure (stream->caps, 0)) && + (encoding = gst_structure_get_string (s, "encoding-name"))) + need_slave = need_slave || (strcmp (encoding, "X-ASF-PF") == 0); + GST_DEBUG_OBJECT (src, "auto buffering mode, need_slave %d", + need_slave); + /* valid duration implies not likely live pipeline, + * so slaving in jitterbuffer does not make much sense + * (and might mess things up due to bursts) */ + if (GST_CLOCK_TIME_IS_VALID (src->segment.duration) && + src->segment.duration && !need_slave) { + GST_DEBUG_OBJECT (src, "selected buffer"); + g_object_set (src->manager, "buffer-mode", BUFFER_MODE_BUFFER, + NULL); + } else { + GST_DEBUG_OBJECT (src, "selected slave"); + g_object_set (src->manager, "buffer-mode", BUFFER_MODE_SLAVE, NULL); + } + } + } /* connect to signals if we did not already do so */ GST_DEBUG_OBJECT (src, "connect to signals on session manager, stream %p", stream); - src->session_sig_id = - g_signal_connect (src->session, "pad-added", - (GCallback) new_session_pad, src); - src->session_ptmap_id = - g_signal_connect (src->session, "request-pt-map", + src->manager_sig_id = + g_signal_connect (src->manager, "pad-added", + (GCallback) new_manager_pad, src); + src->manager_ptmap_id = + g_signal_connect (src->manager, "request-pt-map", (GCallback) request_pt_map, src); - g_signal_connect (src->session, "on-bye-ssrc", (GCallback) on_bye_ssrc, + + g_signal_connect (src->manager, "on-npt-stop", (GCallback) on_npt_stop, src); - g_signal_connect (src->session, "on-bye-timeout", (GCallback) on_timeout, - src); - g_signal_connect (src->session, "on-timeout", (GCallback) on_timeout, - src); - /* FIXME: remove this once the rdtmanager is released */ - if (g_signal_lookup ("on-npt-stop", G_OBJECT_TYPE (src->session)) != 0) { - g_signal_connect (src->session, "on-npt-stop", (GCallback) on_npt_stop, - src); - } else { - GST_INFO_OBJECT (src, "skipping on-npt-stop handling, not implemented"); - } } /* we stream directly to the manager, get some pads. Each RTSP stream goes * into a separate RTP session. */ name = g_strdup_printf ("recv_rtp_sink_%d", stream->id); - stream->channelpad[0] = gst_element_get_request_pad (src->session, name); + stream->channelpad[0] = gst_element_get_request_pad (src->manager, name); g_free (name); name = g_strdup_printf ("recv_rtcp_sink_%d", stream->id); - stream->channelpad[1] = gst_element_get_request_pad (src->session, name); + stream->channelpad[1] = gst_element_get_request_pad (src->manager, name); g_free (name); - /* now configure the bandwidth in the session */ + /* now configure the bandwidth in the manager */ if (g_signal_lookup ("get-internal-session", - G_OBJECT_TYPE (src->session)) != 0) { + G_OBJECT_TYPE (src->manager)) != 0) { GObject *rtpsession; - g_signal_emit_by_name (src->session, "get-internal-session", stream->id, + g_signal_emit_by_name (src->manager, "get-internal-session", stream->id, &rtpsession); if (rtpsession) { GST_INFO_OBJECT (src, "configure bandwidth in session %p", rtpsession); + stream->session = rtpsession; + if (stream->as_bandwidth != -1) { GST_INFO_OBJECT (src, "setting AS: %f", (gdouble) (stream->as_bandwidth * 1000)); @@ -2388,7 +2434,14 @@ gst_rtspsrc_stream_configure_manager (GstRTSPSrc * src, GstRTSPStream * stream, g_object_set (rtpsession, "rtcp-rs-bandwidth", stream->rs_bandwidth, NULL); } - g_object_unref (rtpsession); + g_signal_connect (rtpsession, "on-bye-ssrc", (GCallback) on_bye_ssrc, + stream); + g_signal_connect (rtpsession, "on-bye-timeout", (GCallback) on_timeout, + stream); + g_signal_connect (rtpsession, "on-timeout", (GCallback) on_timeout, + stream); + g_signal_connect (rtpsession, "on-ssrc-active", + (GCallback) on_ssrc_active, stream); } } } @@ -2407,9 +2460,9 @@ manager_failed: GST_DEBUG_OBJECT (src, "no session manager element %s found", manager); return FALSE; } -start_session_failure: +start_manager_failure: { - GST_DEBUG_OBJECT (src, "could not start session"); + GST_DEBUG_OBJECT (src, "could not start session manager"); return FALSE; } } @@ -2497,7 +2550,7 @@ gst_rtspsrc_stream_configure_tcp (GstRTSPSrc * src, GstRTSPStream * stream, gst_object_unref (template); } /* setup RTCP transport back to the server if we have to. */ - if (src->session && src->do_rtcp) { + if (src->manager && src->do_rtcp) { GstPad *pad; template = gst_static_pad_template_get (&anysinktemplate); @@ -2509,7 +2562,7 @@ gst_rtspsrc_stream_configure_tcp (GstRTSPSrc * src, GstRTSPStream * stream, /* get session RTCP pad */ name = g_strdup_printf ("send_rtcp_src_%d", stream->id); - pad = gst_element_get_request_pad (src->session, name); + pad = gst_element_get_request_pad (src->manager, name); g_free (name); /* and link */ @@ -2663,8 +2716,6 @@ gst_rtspsrc_stream_configure_udp (GstRTSPSrc * src, GstRTSPStream * stream, * if we can. */ g_object_set (G_OBJECT (stream->udpsrc[0]), "timeout", src->udp_timeout, NULL); - g_object_set (G_OBJECT (stream->udpsrc[0]), "buffer-size", - src->udp_buffer_size, NULL); /* get output pad of the UDP source. */ *outpad = gst_element_get_static_pad (stream->udpsrc[0], "src"); @@ -2730,7 +2781,7 @@ gst_rtspsrc_stream_configure_udp_sinks (GstRTSPSrc * src, do_rtp = (rtp_port != -1); /* it's possible that the server does not want us to send RTCP in which case * the port is -1 */ - do_rtcp = (rtcp_port != -1 && src->session != NULL && src->do_rtcp); + do_rtcp = (rtcp_port != -1 && src->manager != NULL && src->do_rtcp); /* we need a destination when we have RTP or RTCP ports */ if (destination == NULL && (do_rtp || do_rtcp)) @@ -2749,13 +2800,11 @@ gst_rtspsrc_stream_configure_udp_sinks (GstRTSPSrc * src, goto no_sink_element; /* don't join multicast group, we will have the source socket do that */ - g_object_set (G_OBJECT (stream->udpsink[0]), "auto-multicast", FALSE, NULL); + /* no sync or async state changes needed */ + g_object_set (G_OBJECT (stream->udpsink[0]), "auto-multicast", FALSE, + "loop", FALSE, "sync", FALSE, "async", FALSE, NULL); if (ttl > 0) g_object_set (G_OBJECT (stream->udpsink[0]), "ttl", ttl, NULL); - g_object_set (G_OBJECT (stream->udpsink[0]), "loop", FALSE, NULL); - /* no sync or async state changes needed */ - g_object_set (G_OBJECT (stream->udpsink[0]), "sync", FALSE, "async", FALSE, - NULL); if (stream->udpsrc[0]) { /* configure socket, we give it the same UDP socket as the udpsrc for RTP @@ -2764,8 +2813,8 @@ gst_rtspsrc_stream_configure_udp_sinks (GstRTSPSrc * src, GST_DEBUG_OBJECT (src, "RTP UDP src has sock %d", sockfd); /* configure socket and make sure udpsink does not close it when shutting * down, it belongs to udpsrc after all. */ - g_object_set (G_OBJECT (stream->udpsink[0]), "sockfd", sockfd, NULL); - g_object_set (G_OBJECT (stream->udpsink[0]), "closefd", FALSE, NULL); + g_object_set (G_OBJECT (stream->udpsink[0]), "sockfd", sockfd, + "closefd", FALSE, NULL); } /* the source for the dummy packets to open up NAT */ @@ -2775,9 +2824,7 @@ gst_rtspsrc_stream_configure_udp_sinks (GstRTSPSrc * src, /* random data in 5 buffers, a size of 200 bytes should be fine */ g_object_set (G_OBJECT (stream->fakesrc), "filltype", 3, "num-buffers", 5, - NULL); - g_object_set (G_OBJECT (stream->fakesrc), "sizetype", 2, "sizemax", 200, - "silent", TRUE, NULL); + "sizetype", 2, "sizemax", 200, "silent", TRUE, NULL); /* we don't want to consider this a sink */ GST_OBJECT_FLAG_UNSET (stream->udpsink[0], GST_ELEMENT_IS_SINK); @@ -2804,14 +2851,11 @@ gst_rtspsrc_stream_configure_udp_sinks (GstRTSPSrc * src, goto no_sink_element; /* don't join multicast group, we will have the source socket do that */ - g_object_set (G_OBJECT (stream->udpsink[1]), "auto-multicast", FALSE, NULL); + /* no sync or async state changes needed */ + g_object_set (G_OBJECT (stream->udpsink[1]), "auto-multicast", FALSE, + "loop", FALSE, "sync", FALSE, "async", FALSE, NULL); if (ttl > 0) g_object_set (G_OBJECT (stream->udpsink[0]), "ttl", ttl, NULL); - g_object_set (G_OBJECT (stream->udpsink[1]), "loop", FALSE, NULL); - /* no sync needed */ - g_object_set (G_OBJECT (stream->udpsink[1]), "sync", FALSE, NULL); - /* no async state changes needed */ - g_object_set (G_OBJECT (stream->udpsink[1]), "async", FALSE, NULL); if (stream->udpsrc[1]) { /* configure socket, we give it the same UDP socket as the udpsrc for RTCP @@ -2821,8 +2865,8 @@ gst_rtspsrc_stream_configure_udp_sinks (GstRTSPSrc * src, GST_DEBUG_OBJECT (src, "RTCP UDP src has sock %d", sockfd); /* configure socket and make sure udpsink does not close it when shutting * down, it belongs to udpsrc after all. */ - g_object_set (G_OBJECT (stream->udpsink[1]), "sockfd", sockfd, NULL); - g_object_set (G_OBJECT (stream->udpsink[1]), "closefd", FALSE, NULL); + g_object_set (G_OBJECT (stream->udpsink[1]), "sockfd", sockfd, + "closefd", FALSE, NULL); } /* we don't want to consider this a sink */ @@ -2839,7 +2883,7 @@ gst_rtspsrc_stream_configure_udp_sinks (GstRTSPSrc * src, /* get session RTCP pad */ name = g_strdup_printf ("send_rtcp_src_%d", stream->id); - pad = gst_element_get_request_pad (src->session, name); + pad = gst_element_get_request_pad (src->manager, name); g_free (name); /* and link */ @@ -3016,7 +3060,7 @@ gst_rtspsrc_activate_streams (GstRTSPSrc * src) if (stream->srcpad) { /* if we don't have a session manager, set the caps now. If we have a * session, we will get a notification of the pad and the caps. */ - if (!src->session) { + if (!src->manager) { GST_DEBUG_OBJECT (src, "setting pad caps for stream %p", stream); gst_pad_set_caps (stream->srcpad, stream->caps); } @@ -3084,9 +3128,9 @@ gst_rtspsrc_configure_caps (GstRTSPSrc * src, GstSegment * segment) } GST_DEBUG_OBJECT (src, "stream %p, caps %" GST_PTR_FORMAT, stream, caps); } - if (src->session) { + if (src->manager) { GST_DEBUG_OBJECT (src, "clear session"); - g_signal_emit_by_name (src->session, "clear-pt-map", NULL); + g_signal_emit_by_name (src->manager, "clear-pt-map", NULL); } } @@ -3324,9 +3368,10 @@ gst_rtspsrc_handle_request (GstRTSPSrc * src, GstRTSPConnection * conn, gst_rtsp_message_dump (&response); res = gst_rtspsrc_connection_send (src, conn, &response, NULL); - gst_rtsp_message_unset (&response); if (res < 0) goto send_error; + + gst_rtsp_message_unset (&response); } else if (res == GST_RTSP_EEOF) return res; @@ -3335,6 +3380,7 @@ gst_rtspsrc_handle_request (GstRTSPSrc * src, GstRTSPConnection * conn, /* ERRORS */ send_error: { + gst_rtsp_message_unset (&response); return res; } } @@ -3542,7 +3588,7 @@ gst_rtspsrc_loop_interleaved (GstRTSPSrc * src) src->need_activate = FALSE; } - if (!src->session) { + if (!src->manager) { /* set stream caps on buffer when we don't have a session manager to do it * for us */ gst_buffer_set_caps (buf, stream->caps); @@ -4083,11 +4129,11 @@ gst_rtspsrc_parse_digest_challenge (GstRTSPConnection * conn, g_slist_free (list); } -/* Parse a WWW-Authenticate Response header and determine the +/* Parse a WWW-Authenticate Response header and determine the * available authentication methods * * This code should also cope with the fact that each WWW-Authenticate - * header can contain multiple challenge methods + tokens + * header can contain multiple challenge methods + tokens * * At the moment, for Basic auth, we just do a minimal check and don't * even parse out the realm */ @@ -4116,8 +4162,8 @@ gst_rtspsrc_parse_auth_hdr (gchar * hdr, GstRTSPAuthMethod * methods, * gst_rtspsrc_setup_auth: * @src: the rtsp source * - * Configure a username and password and auth method on the - * connection object based on a response we received from the + * Configure a username and password and auth method on the + * connection object based on a response we received from the * peer. * * Currently, this requires that a username and password were supplied @@ -4341,7 +4387,7 @@ server_eof: GST_ELEMENT_WARNING (src, RESOURCE, READ, (NULL), ("The server closed the connection.")); gst_rtsp_message_unset (response); - return GST_FLOW_UNEXPECTED; + return res; } } @@ -4558,6 +4604,8 @@ gst_rtspsrc_parse_methods (GstRTSPSrc * src, GstRTSPMessage * response) /* always assume PLAY, FIXME, extensions should be able to override * this */ src->methods |= GST_RTSP_PLAY; + /* also assume it will support Range */ + src->seekable = TRUE; /* we need describe and setup */ if (!(src->methods & GST_RTSP_DESCRIBE)) @@ -4908,7 +4956,7 @@ gst_rtspsrc_setup_streams (GstRTSPSrc * src) /* if the user wants a non default RTP packet size we add the blocksize * parameter */ if (src->rtp_blocksize > 0) { - hval = gst_rtspsrc_dup_printf ("%d", src->rtp_blocksize); + hval = g_strdup_printf ("%d", src->rtp_blocksize); gst_rtsp_message_add_header (&request, GST_RTSP_HDR_BLOCKSIZE, hval); g_free (hval); } @@ -5620,7 +5668,7 @@ not_supported: * * url=;[seq=;rtptime=] [, url=...] * - * rtptime corresponds to the timestamp for the NPT time given in the header + * rtptime corresponds to the timestamp for the NPT time given in the header * seqbase corresponds to the next sequence number we received. This number * indicates the first seqnum after the seek and should be used to discard * packets that are from before the seek. @@ -5684,51 +5732,48 @@ gst_rtspsrc_parse_rtpinfo (GstRTSPSrc * src, gchar * rtpinfo) return TRUE; } -#define USE_POSIX_LOCALE { \ - gchar *__old_locale = g_strdup (setlocale (LC_NUMERIC, NULL)); \ - setlocale (LC_NUMERIC, "POSIX"); - -#define RESTORE_LOCALE \ - setlocale (LC_NUMERIC, __old_locale); \ - g_free (__old_locale);} - -static gchar * -gst_rtspsrc_dup_printf (const gchar * format, ...) +static gdouble +gst_rtspsrc_get_float (const gchar * dstr) { - gchar *result; - va_list varargs; + gchar s[G_ASCII_DTOSTR_BUF_SIZE] = { 0, }; - USE_POSIX_LOCALE va_start (varargs, format); - - result = g_strdup_vprintf (format, varargs); - va_end (varargs); - RESTORE_LOCALE return result; -} - -static gint -gst_rtspsrc_get_float (const char *str, gfloat * val) -{ - gint result; - - USE_POSIX_LOCALE result = sscanf (str, "%f", val); - RESTORE_LOCALE return result; + /* canonicalise floating point string so we can handle float strings + * in the form "24.930" or "24,930" irrespective of the current locale */ + g_strlcpy (s, dstr, sizeof (s)); + g_strdelimit (s, ",", '.'); + return g_ascii_strtod (s, NULL); } static gchar * gen_range_header (GstRTSPSrc * src, GstSegment * segment) { - gchar *res; + gchar val_str[G_ASCII_DTOSTR_BUF_SIZE] = { 0, }; if (src->range && src->range->min.type == GST_RTSP_TIME_NOW) { - res = g_strdup_printf ("npt=now-"); + g_strlcpy (val_str, "now", sizeof (val_str)); } else { - if (segment->last_stop == 0) - res = g_strdup_printf ("npt=0-"); - else - res = gst_rtspsrc_dup_printf ("npt=%f-", + if (segment->last_stop == 0) { + g_strlcpy (val_str, "0", sizeof (val_str)); + } else { + g_ascii_dtostr (val_str, sizeof (val_str), ((gdouble) segment->last_stop) / GST_SECOND); + } + } + return g_strdup_printf ("npt=%s-", val_str); +} + +static void +clear_rtp_base (GstRTSPSrc * src, GstRTSPStream * stream) +{ + stream->timebase = -1; + stream->seqbase = -1; + if (stream->caps) { + GstStructure *s; + + stream->caps = gst_caps_make_writable (stream->caps); + s = gst_caps_get_structure (stream->caps, 0); + gst_structure_remove_fields (s, "clock-base", "seqnum-base", NULL); } - return res; } static gboolean @@ -5739,7 +5784,6 @@ gst_rtspsrc_play (GstRTSPSrc * src, GstSegment * segment) GstRTSPResult res; GList *walk; gchar *hval; - gfloat fval; gint hval_idx; gchar *control; @@ -5804,17 +5848,40 @@ gst_rtspsrc_play (GstRTSPSrc * src, GstSegment * segment) } if (segment->rate != 1.0) { - hval = gst_rtspsrc_dup_printf ("%f", segment->rate); + gchar hval[G_ASCII_DTOSTR_BUF_SIZE]; + + g_ascii_dtostr (hval, sizeof (hval), segment->rate); if (src->skip) gst_rtsp_message_add_header (&request, GST_RTSP_HDR_SCALE, hval); else gst_rtsp_message_add_header (&request, GST_RTSP_HDR_SPEED, hval); - g_free (hval); } if (gst_rtspsrc_send (src, conn, &request, &response, NULL) < 0) goto send_error; + /* seek may have silently failed as it is not supported */ + if (!(src->methods & GST_RTSP_PLAY)) { + GST_DEBUG_OBJECT (src, "PLAY Range not supported; re-enable PLAY"); + /* obviously it is supported as we made it here */ + src->methods |= GST_RTSP_PLAY; + src->seekable = FALSE; + /* but there is nothing to parse in the response, + * so convey we have no idea and not to expect anything particular */ + clear_rtp_base (src, stream); + if (control) { + GList *run; + + /* need to do for all streams */ + for (run = src->streams; run; run = g_list_next (run)) + clear_rtp_base (src, (GstRTSPStream *) run->data); + } + /* NOTE the above also disables npt based eos detection */ + /* and below forces position to 0, + * which is visible feedback we lost the plot */ + segment->start = segment->last_stop = 0; + } + gst_rtsp_message_unset (&request); /* parse RTP npt field. This is the current position in the stream (Normal @@ -5830,12 +5897,10 @@ gst_rtspsrc_play (GstRTSPSrc * src, GstSegment * segment) * and should be put in the NEWSEGMENT rate field. */ if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_SPEED, &hval, 0) == GST_RTSP_OK) { - if (gst_rtspsrc_get_float (hval, &fval) > 0) - segment->rate = fval; + segment->rate = gst_rtspsrc_get_float (hval); } else if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_SCALE, &hval, 0) == GST_RTSP_OK) { - if (gst_rtspsrc_get_float (hval, &fval) > 0) - segment->rate = fval; + segment->rate = gst_rtspsrc_get_float (hval); } /* parse the RTP-Info header field (if ANY) to get the base seqnum and timestamp @@ -5871,6 +5936,13 @@ gst_rtspsrc_play (GstRTSPSrc * src, GstSegment * segment) gst_rtspsrc_loop_send_cmd (src, CMD_WAIT, FALSE); gst_task_start (src->task); + /* mark discont */ + GST_DEBUG_OBJECT (src, "mark DISCONT, we did a seek to another position"); + for (walk = src->streams; walk; walk = g_list_next (walk)) { + GstRTSPStream *stream = (GstRTSPStream *) walk->data; + stream->discont = TRUE; + } + done: GST_RTSP_STATE_UNLOCK (src); diff --git a/gst/rtsp/gstrtspsrc.h b/gst/rtsp/gstrtspsrc.h index b1402c2aaa..00861b6da9 100644 --- a/gst/rtsp/gstrtspsrc.h +++ b/gst/rtsp/gstrtspsrc.h @@ -138,6 +138,9 @@ struct _GstRTSPStream { /* per stream connection */ GstRTSPConnInfo conninfo; + /* session */ + GObject *session; + /* bandwidth */ guint as_bandwidth; guint rs_bandwidth; @@ -230,11 +233,12 @@ struct _GstRTSPSrc { /* supported methods */ gint methods; + gboolean seekable; /* session management */ - GstElement *session; - gulong session_sig_id; - gulong session_ptmap_id; + GstElement *manager; + gulong manager_sig_id; + gulong manager_ptmap_id; GstRTSPConnInfo conninfo; diff --git a/gst/udp/gstmultiudpsink.c b/gst/udp/gstmultiudpsink.c index 8a24275225..bcdb6cd0ba 100644 --- a/gst/udp/gstmultiudpsink.c +++ b/gst/udp/gstmultiudpsink.c @@ -44,6 +44,8 @@ GST_DEBUG_CATEGORY_STATIC (multiudpsink_debug); #define GST_CAT_DEFAULT (multiudpsink_debug) +#define UDP_MAX_SIZE 65507 + static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -80,6 +82,7 @@ enum #define DEFAULT_LOOP TRUE #define DEFAULT_QOS_DSCP -1 #define DEFAULT_SEND_DUPLICATES TRUE +#define DEFAULT_BUFFER_SIZE 0 enum { @@ -96,6 +99,7 @@ enum PROP_LOOP, PROP_QOS_DSCP, PROP_SEND_DUPLICATES, + PROP_BUFFER_SIZE, PROP_LAST }; @@ -347,6 +351,11 @@ gst_multiudpsink_class_init (GstMultiUDPSinkClass * klass) "multiple times as well", DEFAULT_SEND_DUPLICATES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_SIZE, + g_param_spec_int ("buffer-size", "Buffer Size", + "Size of the kernel send buffer in bytes, 0=default", 0, G_MAXINT, + DEFAULT_BUFFER_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gstelement_class->change_state = gst_multiudpsink_change_state; gstbasesink_class->render = gst_multiudpsink_render; @@ -489,6 +498,11 @@ gst_multiudpsink_render (GstBaseSink * bsink, GstBuffer * buffer) size = GST_BUFFER_SIZE (buffer); data = GST_BUFFER_DATA (buffer); + if (size > UDP_MAX_SIZE) { + GST_WARNING ("Attempting to send a UDP packet larger than maximum " + "size (%d > %d)", size, UDP_MAX_SIZE); + } + sink->bytes_to_serve += size; /* grab lock while iterating and sending to clients, this should be @@ -579,6 +593,11 @@ gst_multiudpsink_render_list (GstBaseSink * bsink, GstBufferList * list) msg.msg_iov = iov; while ((buf = gst_buffer_list_iterator_next (it))) { + if (GST_BUFFER_SIZE (buf) > UDP_MAX_SIZE) { + GST_WARNING ("Attempting to send a UDP packet larger than maximum " + "size (%d > %d)", GST_BUFFER_SIZE (buf), UDP_MAX_SIZE); + } + msg.msg_iov[msg.msg_iovlen].iov_len = GST_BUFFER_SIZE (buf); msg.msg_iov[msg.msg_iovlen].iov_base = GST_BUFFER_DATA (buf); msg.msg_iovlen++; @@ -773,6 +792,9 @@ gst_multiudpsink_set_property (GObject * object, guint prop_id, case PROP_SEND_DUPLICATES: udpsink->send_duplicates = g_value_get_boolean (value); break; + case PROP_BUFFER_SIZE: + udpsink->buffer_size = g_value_get_int (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -825,6 +847,9 @@ gst_multiudpsink_get_property (GObject * object, guint prop_id, GValue * value, case PROP_SEND_DUPLICATES: g_value_set_boolean (value, udpsink->send_duplicates); break; + case PROP_BUFFER_SIZE: + g_value_set_int (value, udpsink->buffer_size); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -899,6 +924,8 @@ gst_multiudpsink_init_send (GstMultiUDPSink * sink) guint bc_val; GList *clients; GstUDPClient *client; + int sndsize, ret; + socklen_t len; if (sink->sockfd == -1) { GST_DEBUG_OBJECT (sink, "creating sockets"); @@ -914,11 +941,6 @@ gst_multiudpsink_init_send (GstMultiUDPSink * sink) sink->externalfd = FALSE; } else { struct sockaddr_storage myaddr; -#ifdef G_OS_WIN32 - gint len; -#else - guint len; -#endif GST_DEBUG_OBJECT (sink, "using configured socket"); /* we use the configured socket, try to get some info about it */ @@ -932,6 +954,35 @@ gst_multiudpsink_init_send (GstMultiUDPSink * sink) sink->externalfd = TRUE; } + len = sizeof (sndsize); + if (sink->buffer_size != 0) { + sndsize = sink->buffer_size; + + GST_DEBUG_OBJECT (sink, "setting udp buffer of %d bytes", sndsize); + /* set buffer size, Note that on Linux this is typically limited to a + * maximum of around 100K. Also a minimum of 128 bytes is required on + * Linux. */ + ret = + setsockopt (sink->sockfd, SOL_SOCKET, SO_SNDBUF, (void *) &sndsize, + len); + if (ret != 0) { + GST_ELEMENT_WARNING (sink, RESOURCE, SETTINGS, (NULL), + ("Could not create a buffer of requested %d bytes, %d: %s (%d)", + sndsize, ret, g_strerror (errno), errno)); + } + } + + /* read the value of the receive buffer. Note that on linux this returns 2x the + * value we set because the kernel allocates extra memory for metadata. + * The default on Linux is about 100K (which is about 50K without metadata) */ + ret = + getsockopt (sink->sockfd, SOL_SOCKET, SO_SNDBUF, (void *) &sndsize, &len); + if (ret == 0) + GST_DEBUG_OBJECT (sink, "have udp buffer of %d bytes", sndsize); + else + GST_DEBUG_OBJECT (sink, "could not get udp buffer size"); + + bc_val = 1; if (setsockopt (sink->sock, SOL_SOCKET, SO_BROADCAST, &bc_val, sizeof (bc_val)) < 0) diff --git a/gst/udp/gstmultiudpsink.h b/gst/udp/gstmultiudpsink.h index 6d4da77e95..c31dbad83a 100644 --- a/gst/udp/gstmultiudpsink.h +++ b/gst/udp/gstmultiudpsink.h @@ -80,6 +80,7 @@ struct _GstMultiUDPSink { guint16 ss_family; gboolean send_duplicates; + gint buffer_size; }; struct _GstMultiUDPSinkClass { diff --git a/gst/udp/gstudpnetutils.h b/gst/udp/gstudpnetutils.h index 6dd8d75a79..ec863bd7db 100644 --- a/gst/udp/gstudpnetutils.h +++ b/gst/udp/gstudpnetutils.h @@ -32,6 +32,9 @@ #define WINVER 0x0501 #include #include +#ifndef socklen_t +#define socklen_t int +#endif /* Needed for GstObject and GST_WARNING_OBJECT */ #include diff --git a/gst/udp/gstudpsrc.c b/gst/udp/gstudpsrc.c index b76385b069..4363044da2 100644 --- a/gst/udp/gstudpsrc.c +++ b/gst/udp/gstudpsrc.c @@ -41,8 +41,8 @@ * for RTP implementations where the contents of the UDP packets is transfered * out-of-bounds using SDP or other means. * - * The #GstUDPSrc:buffer property is used to change the default kernel buffer - * sizes used for receiving packets. The buffer size may be increased for + * The #GstUDPSrc:buffer-size property is used to change the default kernel + * buffersizes used for receiving packets. The buffer size may be increased for * high-volume connections, or may be decreased to limit the possible backlog of * incoming data. The system places an absolute limit on these values, on Linux, * for example, the default buffer size is typically 50K and can be increased to @@ -50,7 +50,7 @@ * * The #GstUDPSrc:skip-first-bytes property is used to strip off an arbitrary * number of bytes from the start of the raw udp packet and can be used to strip - * off proprietary header, for example. + * off proprietary header, for example. * * The udpsrc is always a live source. It does however not provide a #GstClock, * this is left for upstream elements such as an RTP session manager or demuxer @@ -78,7 +78,7 @@ * because it is blocked by a firewall. * * - * A custom file descriptor can be configured with the + * A custom file descriptor can be configured with the * #GstUDPSrc:sockfd property. The socket will be closed when setting the * element to READY by default. This behaviour can be * overriden with the #GstUDPSrc:closefd property, in which case the application @@ -117,9 +117,6 @@ #endif #include -#ifdef G_OS_WIN32 -typedef int socklen_t; -#endif #ifdef HAVE_FIONREAD_IN_SYS_FILIO #include @@ -778,11 +775,7 @@ gst_udpsrc_start (GstBaseSrc * bsrc) gint ret; int rcvsize; struct sockaddr_storage bind_address; -#ifdef G_OS_WIN32 - gint len; -#else - guint len; -#endif + socklen_t len; src = GST_UDPSRC (bsrc); if (src->sockfd == -1) { diff --git a/gst/videofilter/gstvideobalance.c b/gst/videofilter/gstvideobalance.c index cc27b0ba23..968e2596c2 100644 --- a/gst/videofilter/gstvideobalance.c +++ b/gst/videofilter/gstvideobalance.c @@ -44,21 +44,14 @@ #include "config.h" #endif +#include + #include "gstvideobalance.h" #include -#include #include #include -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -#ifdef WIN32 -#define rint(x) (floor((x)+0.5)) -#endif - GST_DEBUG_CATEGORY_STATIC (videobalance_debug); #define GST_CAT_DEFAULT videobalance_debug @@ -170,8 +163,8 @@ gst_video_balance_update_tables (GstVideoBalance * vb) vb->tabley[i] = rint (y); } - hue_cos = cos (M_PI * vb->hue); - hue_sin = sin (M_PI * vb->hue); + hue_cos = cos (G_PI * vb->hue); + hue_sin = sin (G_PI * vb->hue); /* U/V lookup tables are 2D, since we need both U/V for each table * separately. */ diff --git a/po/af.po b/po/af.po index b1df978f63..138c08481f 100644 --- a/po/af.po +++ b/po/af.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins 0.7.6\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2004-03-18 14:16+0200\n" "Last-Translator: Petri Jooste \n" "Language-Team: Afrikaans \n" @@ -70,10 +70,13 @@ msgstr "" msgid "This file is invalid and cannot be played." msgstr "" -msgid "This file is incomplete and cannot be played." +msgid "This file is corrupt and cannot be played." msgstr "" -msgid "This file is corrupt and cannot be played." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." msgstr "" msgid "The video in this file might not play correctly." @@ -626,6 +629,15 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Kon nie oudio-toestel \"%s\" toemaak nie." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "Kon nie genoeg buffers vanaf toestel \"%s\" kry nie." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Kon nie oudio-toestel \"%s\" toemaak nie." + #, fuzzy, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Kon nie buffers vanaf toestel \"%s\" verkry nie." diff --git a/po/az.po b/po/az.po index d51b991e8a..f51c1cb74c 100644 --- a/po/az.po +++ b/po/az.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-0.8.0\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2004-03-19 18:29+0200\n" "Last-Translator: Metin Amiroff \n" "Language-Team: Azerbaijani \n" @@ -71,10 +71,13 @@ msgstr "" msgid "This file is invalid and cannot be played." msgstr "" -msgid "This file is incomplete and cannot be played." +msgid "This file is corrupt and cannot be played." msgstr "" -msgid "This file is corrupt and cannot be played." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." msgstr "" msgid "The video in this file might not play correctly." @@ -627,6 +630,15 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "\"%s\" audio avadanlığı bağlana bilmədi." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "\"%s\" avadanlığından kifayət qədər bufferlər alına bilmədi." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "\"%s\" audio avadanlığı bağlana bilmədi." + #, fuzzy, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "\"%s\" avadanlığından bufferlər alına bilmədi." diff --git a/po/bg.po b/po/bg.po index 32c4cacacf..da3786aa97 100644 --- a/po/bg.po +++ b/po/bg.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.25.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-18 00:43+0000\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-11-04 14:23+0200\n" "Last-Translator: Alexander Shopov \n" "Language-Team: Bulgarian \n" @@ -69,12 +69,15 @@ msgstr "Този файл не съдържа изпълними потоци." msgid "This file is invalid and cannot be played." msgstr "Този файл е повреден и не може да бъде изпълнен." -msgid "This file is incomplete and cannot be played." -msgstr "Този файл е непълен и не може да бъде изпълнен." - msgid "This file is corrupt and cannot be played." msgstr "Този файл е повреден и не може да бъде изпълнен." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Този файл е непълен и не може да бъде изпълнен." + msgid "The video in this file might not play correctly." msgstr "Видео потокът в този файл може да не се покаже правилно." @@ -627,6 +630,16 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Неуспех при задаването на вход %d на устройство „%s“." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Неуспех при получаването на текущия вход на устройство „%s“. Може би е радио." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Неуспех при задаването на вход %d на устройство „%s“." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Буферите в устройство „%s“ не могат да се подредят в опашка." diff --git a/po/ca.po b/po/ca.po index df0a28115e..8ceac115f9 100644 --- a/po/ca.po +++ b/po/ca.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.9.7\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2005-12-04 21:54+0100\n" "Last-Translator: Jordi Mallach \n" "Language-Team: Catalan \n" @@ -67,10 +67,13 @@ msgstr "" msgid "This file is invalid and cannot be played." msgstr "" -msgid "This file is incomplete and cannot be played." +msgid "This file is corrupt and cannot be played." msgstr "" -msgid "This file is corrupt and cannot be played." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." msgstr "" msgid "The video in this file might not play correctly." @@ -619,6 +622,15 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "" +#, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" + +#, c-format +msgid "Failed to set output %d on device %s." +msgstr "" + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "" diff --git a/po/cs.po b/po/cs.po index 4158ec74fc..d91a5ee220 100644 --- a/po/cs.po +++ b/po/cs.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good-0.10.25.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-01 17:27+0000\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-10-31 10:21+0100\n" "Last-Translator: Petr Kovar \n" "Language-Team: Czech \n" @@ -70,12 +70,15 @@ msgstr "Tento soubor neobsahuje hratelné proudy." msgid "This file is invalid and cannot be played." msgstr "Tento soubor je neplatný a nelze jej přehrát." -msgid "This file is incomplete and cannot be played." -msgstr "Tento soubor není úplný a nelze jej přehrát." - msgid "This file is corrupt and cannot be played." msgstr "Tento soubor je poškozen a nelze jej přehrát." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Tento soubor není úplný a nelze jej přehrát." + msgid "The video in this file might not play correctly." msgstr "Video v tomto souboru se nemusí přehrát správně." @@ -629,6 +632,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Nezdařilo se nastavení vstupu \"%d\" na zařízení \"%s\"." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Nezdařilo se získání aktuálního vstupu na zařízení \"%s\". Možná se jedná o " +"radiopřijímač." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Nezdařilo se nastavení vstupu \"%d\" na zařízení \"%s\"." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Nezdařilo se zařazení vyrovnávací paměti na zařízení \"%s\"." diff --git a/po/da.po b/po/da.po index 2d5704e21f..8d48f151e6 100644 --- a/po/da.po +++ b/po/da.po @@ -3,17 +3,17 @@ # This file is distributed under the same license as the gst-plugins-good package. # # Mogens Jaeger , 2007. -# Joe Hansen , 2008, 2009, 2010. +# Joe Hansen , 2008, 2009, 2010, 2011. # # gain -> forhøjelse # boost -> øgning? # msgid "" msgstr "" -"Project-Id-Version: gst-plugins-good-0.10.25.3\n" +"Project-Id-Version: gst-plugins-good-0.10.26.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-01 17:27+0000\n" -"PO-Revision-Date: 2010-10-26 23:54+0200\n" +"POT-Creation-Date: 2011-01-11 19:32+0000\n" +"PO-Revision-Date: 2011-01-07 23:54+0200\n" "Last-Translator: Joe Hansen \n" "Language-Team: Danish \n" "Language: da\n" @@ -73,12 +73,15 @@ msgstr "Denne fil indeholder ingen spilbar strøm." msgid "This file is invalid and cannot be played." msgstr "Denne fil er ufuldstændig og kan ikke afspilles." -msgid "This file is incomplete and cannot be played." -msgstr "Denne fil er ufuldstændig og kan ikke afspilles." - msgid "This file is corrupt and cannot be played." msgstr "Denne fil er ødelagt og kan ikke afspilles." +msgid "Invalid atom size." +msgstr "Ugyldig atomstørrelse." + +msgid "This file is incomplete and cannot be played." +msgstr "Denne fil er ufuldstændig og kan ikke afspilles." + msgid "The video in this file might not play correctly." msgstr "Videoen i denne fil afspilles måske ikke korrekt." @@ -634,6 +637,16 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Kunne ikke sætte inddata %d for enhed %s." +#, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Kunne ikke hente nuværende uddata for enhed '%s'. Måske er det en radioenhed" + +#, c-format +msgid "Failed to set output %d on device %s." +msgstr "Kunne ikke sætte uddata %d for enhed %s." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Kunne ikke sætte mellemlager fra enhed '%s' i kø." diff --git a/po/de.po b/po/de.po index 13a0ad6344..12e2c13286 100644 --- a/po/de.po +++ b/po/de.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.25.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-01 17:27+0000\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-10-25 00:33+0200\n" "Last-Translator: Christian Kirbach \n" "Language-Team: German \n" @@ -79,12 +79,15 @@ msgstr "Diese Datei enthält keine abspielbaren Ströme." msgid "This file is invalid and cannot be played." msgstr "Diese Datei ist ungültig und kann nicht wiedergegeben werden." -msgid "This file is incomplete and cannot be played." -msgstr "Diese Datei ist unvollständig und kann nicht wiedergegeben werden." - msgid "This file is corrupt and cannot be played." msgstr "Diese Datei ist beschädigt und kann nicht wiedergegeben werden." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Diese Datei ist unvollständig und kann nicht wiedergegeben werden." + msgid "The video in this file might not play correctly." msgstr "Das Video in dieser Datei wird vielleicht nicht korrekt wiedergegeben." @@ -643,6 +646,18 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Festlegen der Eingabe »%d« des Geräts »%s« schlug fehl." +# Sendegerät sicherlich nicht, eben eher ein Radio. Warum diese Fehlermeldung beie einem Radiogerät kommen könnte, weiß ich allerdings auch nicht. +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Auslesen der aktuellen Eingabe auf dem Gerät »%s« schlug fehl. Vielleicht " +"ist es ein Radiogerät." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Festlegen der Eingabe »%d« des Geräts »%s« schlug fehl." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Auf Gerät »%s« konnten keine Puffer eingereiht werden." diff --git a/po/el.po b/po/el.po index e14b1e8944..1423f2d221 100644 --- a/po/el.po +++ b/po/el.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.25.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-01 17:27+0000\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-10-27 12:16+0200\n" "Last-Translator: Michael Kotsarinis \n" "Language-Team: Greek \n" @@ -70,12 +70,15 @@ msgstr "Αυτό το αρχείο δεν περιέχει αναπαραγώγ msgid "This file is invalid and cannot be played." msgstr "Το αρχείο αυτό δεν είναι έγκυρο και δεν μπορεί να αναπαραχθεί." -msgid "This file is incomplete and cannot be played." -msgstr "Το αρχείο αυτό είναι ανολοκλήρωτο και δεν μπορεί να αναπαραχθεί." - msgid "This file is corrupt and cannot be played." msgstr "Το αρχείο αυτό είναι κατεστραμμένο και δεν μπορεί να αναπαραχθεί. " +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Το αρχείο αυτό είναι ανολοκλήρωτο και δεν μπορεί να αναπαραχθεί." + msgid "The video in this file might not play correctly." msgstr "Το βίντεο σε αυτό το αρχείο μπορεί να μην αναπαραχθεί σωστά." @@ -631,6 +634,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Αποτυχία ρύθμισης εισαγωγής %d στην συσκευή %s." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Αποτυχία ανάγνωσης της τρέχουσας εισαγωγής στην συσκευή '%s'. Πιθανόν να " +"είναι μια συσκευή ράδιο" + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Αποτυχία ρύθμισης εισαγωγής %d στην συσκευή %s." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Αδυναμία θέσης σε σειρά των buffer στη συσκευή '%s'." diff --git a/po/en_GB.po b/po/en_GB.po index 156b7fc141..d3fe92c4cc 100644 --- a/po/en_GB.po +++ b/po/en_GB.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins 0.8.1\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2004-04-26 10:41-0400\n" "Last-Translator: Gareth Owen \n" "Language-Team: English (British) \n" @@ -70,10 +70,13 @@ msgstr "" msgid "This file is invalid and cannot be played." msgstr "" -msgid "This file is incomplete and cannot be played." +msgid "This file is corrupt and cannot be played." msgstr "" -msgid "This file is corrupt and cannot be played." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." msgstr "" msgid "The video in this file might not play correctly." @@ -626,6 +629,15 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Could not close audio device \"%s\"." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "Could not get enough buffers from device \"%s\"." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Could not close audio device \"%s\"." + #, fuzzy, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Could not get buffers from device \"%s\"." diff --git a/po/es.po b/po/es.po index bf412870b7..bb8ac8a2a1 100644 --- a/po/es.po +++ b/po/es.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.25.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-01 17:27+0000\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-11-01 18:05+0100\n" "Last-Translator: Jorge González González \n" "Language-Team: Spanish \n" @@ -70,12 +70,15 @@ msgstr "Este archivo no contiene flujos reproducibles." msgid "This file is invalid and cannot be played." msgstr "Este archivo no es válido y no se puede reproducir." -msgid "This file is incomplete and cannot be played." -msgstr "Este archivo está incompleto y no se puede reproducir." - msgid "This file is corrupt and cannot be played." msgstr "Este archivo está corrupto y no se puede reproducir." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Este archivo está incompleto y no se puede reproducir." + msgid "The video in this file might not play correctly." msgstr "" "Puede que el vídeo en este archivo no se pueda reproducir correctamente." @@ -636,6 +639,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Falló al establecer la entrada %d en el dispositivo %s." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Falló al obtener la entrada actual en el dispositivo «%s». Quizá sea un " +"dispositivo de radio." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Falló al establecer la entrada %d en el dispositivo %s." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "No se pueden encolar los búferes en el dispositivo «%s»." diff --git a/po/eu.po b/po/eu.po index 283db27ce5..02d9f8965b 100644 --- a/po/eu.po +++ b/po/eu.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good-0.10.18.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-03-25 12:37+0100\n" "Last-Translator: Mikel Olasagasti Uranga \n" "Language-Team: Basque \n" @@ -74,12 +74,15 @@ msgstr "Fitxategi horretan ez dago erreproduzi daitekeen korronterik." msgid "This file is invalid and cannot be played." msgstr "Fitxategi hau ez da baliozkoa eta ezin da erreproduzitu." -msgid "This file is incomplete and cannot be played." -msgstr "Fitxategi hau osatu gabea dago, eta ezin da erreproduzitu." - msgid "This file is corrupt and cannot be played." msgstr "Fitxategi hau hondatua dago, eta ezin da erreproduzitu." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Fitxategi hau osatu gabea dago, eta ezin da erreproduzitu." + msgid "The video in this file might not play correctly." msgstr "" "Litekeena da fitxategi honetako bideoa ez behar bezala erreproduzitzea." @@ -634,6 +637,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Huts egin du '%2$s' gailuko %1$d. sarrera ezartzean." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Huts egin du '%s' gailuko uneko sarrera eskuratzean. Litekeena da irrati-" +"gailu bat izatea." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Huts egin du '%2$s' gailuko %1$d. sarrera ezartzean." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Ezin izan dira bufferrak ilaran jarri '%s' gailuan." diff --git a/po/fi.po b/po/fi.po index 3e0104a5f3..a659e8d0eb 100644 --- a/po/fi.po +++ b/po/fi.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.25.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-18 00:43+0000\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-11-17 23:03+0200\n" "Last-Translator: Tommi Vainikainen \n" "Language-Team: Finnish \n" @@ -74,12 +74,15 @@ msgstr "Tiedosto ei sisällä soitettavia virtoja." msgid "This file is invalid and cannot be played." msgstr "Tiedosto on virheellinen eikä sitä voida esittää." -msgid "This file is incomplete and cannot be played." -msgstr "Tiedosto on vajaa eikä sitä voida esittää." - msgid "This file is corrupt and cannot be played." msgstr "Tiedosto on vioittunut eikä sitä voida näyttää." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Tiedosto on vajaa eikä sitä voida esittää." + msgid "The video in this file might not play correctly." msgstr "Tiedostossa olevaa videota ei ehkä voida näyttää oikein." @@ -628,6 +631,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Sisääntuloa %d ei voitu asettaa laitteelle %s." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Laitteen ”%s” tämänhetkistä sisääntuloa ei voitu lukea, se ei ehkä ole " +"radiolaite" + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Sisääntuloa %d ei voitu asettaa laitteelle %s." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Puskureita ei voitu laittaa jonoon laitteella ”%s”." diff --git a/po/fr.po b/po/fr.po index 6e527dc448..a3220e74d3 100644 --- a/po/fr.po +++ b/po/fr.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.25.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-01 17:27+0000\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-10-27 09:48+0200\n" "Last-Translator: Claude Paroz \n" "Language-Team: French \n" @@ -72,12 +72,15 @@ msgstr "Ce fichier ne contient aucun flux exploitable." msgid "This file is invalid and cannot be played." msgstr "Ce fichier n'est pas valide et ne peut donc pas être lu." -msgid "This file is incomplete and cannot be played." -msgstr "Ce fichier n'est pas complet et ne peut donc pas être lu." - msgid "This file is corrupt and cannot be played." msgstr "Ce fichier est corrompu et ne peut pas être lu." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Ce fichier n'est pas complet et ne peut donc pas être lu." + msgid "The video in this file might not play correctly." msgstr "" "Il est possible que la vidéo de ce fichier ne puisse pas être lue " @@ -637,6 +640,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Impossible de définir l'entrée %d du périphérique %s." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Impossible d'obtenir l'entrée actuelle du périphérique « %s ». C'est peut-" +"être un périphérique radio" + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Impossible de définir l'entrée %d du périphérique %s." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "" diff --git a/po/gl.po b/po/gl.po index f1753f619e..b34bcbaafe 100644 --- a/po/gl.po +++ b/po/gl.po @@ -1,21 +1,21 @@ # Galician translation of gst-plugins-good. # Copyright (C) 2009 gst-plugins-good's COPYRIGHT HOLDER # This file is distributed under the same license as the gst-plugins-good package. -# Fran Diéguez , 2009, 2010. +# Fran Diéguez , 2009, 2010, 2011. # msgid "" msgstr "" -"Project-Id-Version: gst-plugins-good 0.10.23.2\n" +"Project-Id-Version: gst-plugins-good 0.10.26.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" -"PO-Revision-Date: 2010-08-12 15:46+0200\n" +"POT-Creation-Date: 2011-01-11 19:32+0000\n" +"PO-Revision-Date: 2011-01-09 21:20+0100\n" "Last-Translator: Fran Diéguez \n" "Language-Team: Galician \n" "Language: gl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n!=1);\\\n" +"Plural-Forms: nplurals=2; plural=(n!=1);\n" msgid "Could not establish connection to sound server" msgstr "Non foi posíbel estabelecer a conexión co servidor de son" @@ -38,28 +38,28 @@ msgid "Could not connect to server" msgstr "Non foi posíbel conectarse ao servidor" msgid "Server does not support seeking." -msgstr "" +msgstr "O servidor non admite a busca." -#, fuzzy msgid "Could not resolve server name." -msgstr "Non foi posíbel conectarse ao servidor" +msgstr "Non foi posíbel resolver o nome do servidor." -#, fuzzy msgid "Could not establish connection to server." -msgstr "Non foi posíbel estabelecer a conexión co servidor de son" +msgstr "Non foi posíbel estabelecer a conexión co servidor." msgid "Secure connection setup failed." -msgstr "" +msgstr "Produciuse un fallo de configuración da conexión segura." msgid "" "A network error occured, or the server closed the connection unexpectedly." msgstr "" +"Produciuse un erro de rede ou o servidor pechou a conexión de forma " +"inesperada." msgid "Server sent bad data." -msgstr "" +msgstr "O servidor enviou datos erróneos." msgid "No URL set." -msgstr "" +msgstr "No existe un URL estabelecido." msgid "No or invalid input audio, AVI stream will be corrupt." msgstr "O audio non existe ou non é válido, o fluxo AVI está corrompido." @@ -70,12 +70,15 @@ msgstr "Este ficheiro non contén ningún fluxo reproducíbel." msgid "This file is invalid and cannot be played." msgstr "Este ficheiro é incorrecto e non pode reproducirse." -msgid "This file is incomplete and cannot be played." -msgstr "Este ficheiro está incompleto e non pode reproducirse." - msgid "This file is corrupt and cannot be played." msgstr "Este ficheiro está danado e non pode reproducirse." +msgid "Invalid atom size." +msgstr "Tamaño atom non válido." + +msgid "This file is incomplete and cannot be played." +msgstr "Este ficheiro está incompleto e non pode reproducirse." + msgid "The video in this file might not play correctly." msgstr "Este vídeo neste ficheiro podería non reproducirse correctamente." @@ -205,44 +208,41 @@ msgstr "" msgid "Could not open audio device for recording." msgstr "Non foi posíbel abrir o dispositivo de son para a gravación." -#, fuzzy msgid "Could not open audio device for mixer control handling." -msgstr "Non foi posíbel abrir o dispositivo de son para a gravación." +msgstr "" +"Non foi posíbel abrir o dispositivo de son para a súa xestión polo control " +"de misturado." -#, fuzzy msgid "" "Could not open audio device for mixer control handling. This version of the " "Open Sound System is not supported by this element." msgstr "" -"Non foi posíbel abrir o dispositivo de son para a gravación. Vostede non ten " -"permisos para abrir o dispositivo." +"Non foi posíbel abrir o dispositivo de son para a súa xestión polo control " +"de misturado. Este elemento non admite esta versión do Open Sound System." msgid "Master" -msgstr "" +msgstr "Mestre" msgid "Front" -msgstr "" +msgstr "Frontal" -#, fuzzy msgid "Rear" -msgstr "Gravar" +msgstr "Treseiro" -#, fuzzy msgid "Headphones" -msgstr "Cascos" +msgstr "Auriculares" msgid "Center" -msgstr "" +msgstr "Centro" msgid "LFE" -msgstr "" +msgstr "LFE" msgid "Surround" -msgstr "" +msgstr "Envolvente" -#, fuzzy msgid "Side" -msgstr "Vídeo" +msgstr "Lateral" msgid "Built-in Speaker" msgstr "Altofalante interno" @@ -253,308 +253,286 @@ msgstr "Saída AUX 1" msgid "AUX 2 Out" msgstr "Saída AUX 2" -#, fuzzy msgid "AUX Out" -msgstr "Saída AUX 1" +msgstr "Saída AUX" msgid "3D Depth" -msgstr "" +msgstr "Profundidade 3D" msgid "3D Center" -msgstr "" +msgstr "Centro 3D" msgid "3D Enhance" -msgstr "" +msgstr "Mellora 3D" -#, fuzzy msgid "Telephone" -msgstr "Cascos" +msgstr "Teléfono" msgid "Line Out" msgstr "Liña de saída" -#, fuzzy msgid "Line In" msgstr "Liña de entrada" msgid "Internal CD" -msgstr "" +msgstr "CD interno" -#, fuzzy msgid "Video In" -msgstr "Vídeo" +msgstr "Entrada de vídeo" -#, fuzzy msgid "AUX 1 In" -msgstr "Saída AUX 1" +msgstr "Entrada AUX 1" -#, fuzzy msgid "AUX 2 In" -msgstr "Saída AUX 2" +msgstr "Entrada AUX 2" msgid "AUX In" -msgstr "" +msgstr "Entrada AUX" -#, fuzzy msgid "Record Gain" -msgstr "Gravar" +msgstr "Ganancia de gravación" -#, fuzzy msgid "Output Gain" msgstr "Ganancia de saída" -#, fuzzy msgid "Microphone Boost" -msgstr "Micrófono" +msgstr "Aumento do micrófono" msgid "Loopback" -msgstr "" +msgstr "Bucle local" msgid "Diagnostic" -msgstr "" +msgstr "Diagnóstico" msgid "Bass Boost" -msgstr "" +msgstr "Aumento de baixos" msgid "Playback Ports" -msgstr "" +msgstr "Portos de reprodución" msgid "Input" -msgstr "" +msgstr "Entrada" -#, fuzzy msgid "Record Source" -msgstr "Gravar" +msgstr "Orixe da gravación" -#, fuzzy msgid "Monitor Source" -msgstr "Monitor" +msgstr "Monitor de orixe" msgid "Keyboard Beep" -msgstr "" +msgstr "Pitido de teclado" msgid "Simulate Stereo" -msgstr "" +msgstr "Simular estéreo" msgid "Stereo" -msgstr "" +msgstr "Estéreo" msgid "Surround Sound" -msgstr "" +msgstr "Son envolvente" -#, fuzzy msgid "Microphone Gain" -msgstr "Micrófono" +msgstr "Ganancia do micrófono" -#, fuzzy msgid "Speaker Source" -msgstr "Altofalante" +msgstr "Altofalante de orixe" -#, fuzzy msgid "Microphone Source" -msgstr "Micrófono" +msgstr "Micrófono de orixe" msgid "Jack" -msgstr "" +msgstr "Jack" msgid "Center / LFE" -msgstr "" +msgstr "Centro / LFE" msgid "Stereo Mix" -msgstr "" +msgstr "Misturador estéreo" msgid "Mono Mix" -msgstr "" +msgstr "Misturador mono" msgid "Input Mix" -msgstr "" +msgstr "Misturador de entrada" -#, fuzzy msgid "SPDIF In" -msgstr "Saída SPDIF" +msgstr "Entrada SPDIF" msgid "SPDIF Out" msgstr "Saída SPDIF" -#, fuzzy msgid "Microphone 1" -msgstr "Micrófono" +msgstr "Micrófono 1" -#, fuzzy msgid "Microphone 2" -msgstr "Micrófono" +msgstr "Micrófono 2" -#, fuzzy msgid "Digital Out" -msgstr "Dixital 1" +msgstr "Saída dixital" -#, fuzzy msgid "Digital In" -msgstr "Dixital 1" +msgstr "Entrada dixital" msgid "HDMI" -msgstr "" +msgstr "HDMI" msgid "Modem" -msgstr "" +msgstr "Módem" msgid "Handset" -msgstr "" +msgstr "Auriculares" msgid "Other" -msgstr "" +msgstr "Outro" msgid "None" -msgstr "" +msgstr "Ningún" msgid "On" -msgstr "" +msgstr "Acendido" msgid "Off" -msgstr "" +msgstr "Apagado" msgid "Mute" -msgstr "" +msgstr "Silenciar" msgid "Fast" -msgstr "" +msgstr "Rápido" #. TRANSLATORS: "Very Low" is a quality setting here msgid "Very Low" -msgstr "" +msgstr "Moi baixo" #. TRANSLATORS: "Low" is a quality setting here msgid "Low" -msgstr "" +msgstr "Baixo" #. TRANSLATORS: "Medium" is a quality setting here msgid "Medium" -msgstr "" +msgstr "Medio" #. TRANSLATORS: "High" is a quality setting here msgid "High" -msgstr "" +msgstr "Alto" #. TRANSLATORS: "Very High" is a quality setting here msgid "Very High" -msgstr "" +msgstr "Moi alto" #. TRANSLATORS: "Production" is a quality setting here msgid "Production" -msgstr "" +msgstr "Produción" -#, fuzzy msgid "Front Panel Microphone" -msgstr "Micrófono" +msgstr "Micrófono do panel frontal" msgid "Front Panel Line In" -msgstr "" +msgstr "Entrada do panel frontal" msgid "Front Panel Headphones" -msgstr "" +msgstr "Auriculares do panel frontal" msgid "Front Panel Line Out" -msgstr "" +msgstr "Saída do panel frontal" msgid "Green Connector" -msgstr "" +msgstr "Conectador verde" msgid "Pink Connector" -msgstr "" +msgstr "Conectador rosa" msgid "Blue Connector" -msgstr "" +msgstr "Conectador azul" msgid "White Connector" -msgstr "" +msgstr "Conectador branco" msgid "Black Connector" -msgstr "" +msgstr "Conectador negro" msgid "Gray Connector" -msgstr "" +msgstr "Conectador gris" msgid "Orange Connector" -msgstr "" +msgstr "Conectador laranxa" msgid "Red Connector" -msgstr "" +msgstr "Conectador vermello" msgid "Yellow Connector" -msgstr "" +msgstr "Conectador amarelo" msgid "Green Front Panel Connector" -msgstr "" +msgstr "Conectador verde do panel frontal" msgid "Pink Front Panel Connector" -msgstr "" +msgstr "Conectador rosa do panel frontal" msgid "Blue Front Panel Connector" -msgstr "" +msgstr "Conectador azul do panel frontal" msgid "White Front Panel Connector" -msgstr "" +msgstr "Conectador branco do panel frontal" msgid "Black Front Panel Connector" -msgstr "" +msgstr "Conectador negro do panel frontal" msgid "Gray Front Panel Connector" -msgstr "" +msgstr "Conectador gris do panel frontal" msgid "Orange Front Panel Connector" -msgstr "" +msgstr "Conectador laranxado panel frontal" msgid "Red Front Panel Connector" -msgstr "" +msgstr "Conectador vermello do panel frontal" msgid "Yellow Front Panel Connector" -msgstr "" +msgstr "Conectador amarelo do panel frontal" msgid "Spread Output" -msgstr "" +msgstr "Expandir saída" msgid "Downmix" -msgstr "" +msgstr "Redución de canles" msgid "Virtual Mixer Input" -msgstr "" +msgstr "Entrada do misturador virtual" msgid "Virtual Mixer Output" -msgstr "" +msgstr "Saída do misturador virtual" msgid "Virtual Mixer Channels" -msgstr "" +msgstr "Canles do misturador virtual" #. TRANSLATORS: name + number of a volume mixer control #, c-format msgid "%s %d Function" -msgstr "" +msgstr "Función %s %d" #. TRANSLATORS: name of a volume mixer control #, c-format msgid "%s Function" -msgstr "" +msgstr "Función %s" -#, fuzzy msgid "" "Could not open audio device for playback. This version of the Open Sound " "System is not supported by this element." msgstr "" -"Non foi posíbel abrir o dispositivo de son. Vostede non ten permisos para " -"abrir o dispositivo." +"Non foi posíbel abrir o dispositivo de son para a reprodución. Esta versión " +"do Open Sound System non está admitida por este elemento." msgid "Playback is not supported by this audio device." -msgstr "" +msgstr "Este dispositivo de son non admite a reprodución." msgid "Audio playback error." -msgstr "" +msgstr "Erro de reprodución de son." msgid "Recording is not supported by this audio device." -msgstr "" +msgstr "Este dispositivo de son non admite a gravación." -#, fuzzy msgid "Error recording from audio device." -msgstr "Produciuse un erro ao ler %d bytes desde o dispositivo «%s»." +msgstr "Erro ao gravar do dispositivo de son." msgid "Gain" msgstr "Ganancia" @@ -663,6 +641,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Produciuse un fallo ao estabelecer a entrada %d no dispositivo %s." +#, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Produciuse un fallo ao obter a entrada actual no dispositivo «%s». Quizais " +"sexa un dispositivo de radio" + +#, c-format +msgid "Failed to set output %d on device %s." +msgstr "Produciuse un fallo ao estabelecer a saída %d no dispositivo %s." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Non é posíbel meter na cola os búferes no dispositivo «%s»." diff --git a/po/hu.po b/po/hu.po index 8c43eb4446..4b92d18916 100644 --- a/po/hu.po +++ b/po/hu.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.25.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-18 00:43+0000\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-11-04 01:21+0100\n" "Last-Translator: Gabor Kelemen \n" "Language-Team: Hungarian \n" @@ -73,12 +73,15 @@ msgstr "A fájl nem tartalmaz lejátszható adatfolyamokat." msgid "This file is invalid and cannot be played." msgstr "A fájl nem érvényes és nem játszható le." -msgid "This file is incomplete and cannot be played." -msgstr "A fájl nem teljes és nem játszható le." - msgid "This file is corrupt and cannot be played." msgstr "A fájl sérült és nem játszható le." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "A fájl nem teljes és nem játszható le." + msgid "The video in this file might not play correctly." msgstr "A fájlban található videó lehet, hogy nem játszható le megfelelően." @@ -631,6 +634,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "A(z) %d. bemenet beállítása meghiúsult a(z) „%s” eszközön." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Az aktuális bemenet lekérése meghiúsult a(z) „%s” eszközről. Lehet, hogy ez " +"egy rádióeszköz." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "A(z) %d. bemenet beállítása meghiúsult a(z) „%s” eszközön." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Nem állíthatók sorba a pufferek a(z) „%s” eszközben." diff --git a/po/id.po b/po/id.po index aa92786aac..ab9e8132d4 100644 --- a/po/id.po +++ b/po/id.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.23.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-06-29 22:42+0700\n" "Last-Translator: Andhika Padmawan \n" "Language-Team: Indonesian \n" @@ -70,12 +70,15 @@ msgstr "Berkas ini tidak berisi arus yang dapat diputar." msgid "This file is invalid and cannot be played." msgstr "Berkas ini tidak sah dan tak dapat diputar." -msgid "This file is incomplete and cannot be played." -msgstr "Berkas ini tidak lengkap dan tak dapat diputar." - msgid "This file is corrupt and cannot be played." msgstr "Berkas ini rusak dan tak dapat diputar." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Berkas ini tidak lengkap dan tak dapat diputar." + msgid "The video in this file might not play correctly." msgstr "Video di berkas ini mungkin tak dapat diputar dengan benar." @@ -623,6 +626,16 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Gagal mengatur masukan %d di divais %s." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Gagal mendapatkan masukan terkini di divais '%s'. Mungkin itu divais radio" + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Gagal mengatur masukan %d di divais %s." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Tak dapat mengantrekan penyangga di divais '%s'." diff --git a/po/it.po b/po/it.po index 798a735b48..63f9bf0c23 100644 --- a/po/it.po +++ b/po/it.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.25.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-01 17:27+0000\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-10-25 10:11+0200\n" "Last-Translator: Luca Ferretti \n" "Language-Team: Italian \n" @@ -70,12 +70,15 @@ msgstr "Questo file non contiene alcuno stream riproducibile." msgid "This file is invalid and cannot be played." msgstr "Questo file non è valido e non può essere riprodotto." -msgid "This file is incomplete and cannot be played." -msgstr "Questo file è incompleto e non può essere riprodotto." - msgid "This file is corrupt and cannot be played." msgstr "Questo file è alterato e non può essere riprodotto." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Questo file è incompleto e non può essere riprodotto." + msgid "The video in this file might not play correctly." msgstr "Il video in questo file potrebbe non essere riprodotto correttamente." @@ -645,6 +648,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Impostazione dell'ingresso %d sul device «%s» non riuscita." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Recupero dell'attuale ingresso sul device «%s» non riuscito. Forse è un " +"dispositivo radio" + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Impostazione dell'ingresso %d sul device «%s» non riuscita." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Impossibile accodare i buffer nel device «%s»." diff --git a/po/ja.po b/po/ja.po index 2e2b19c696..45d44cf5ec 100644 --- a/po/ja.po +++ b/po/ja.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.14.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2009-06-01 19:29+0900\n" "Last-Translator: Makoto Kato \n" "Language-Team: Japanese \n" @@ -73,12 +73,15 @@ msgstr "このファイルには再生不可能なストリームが含まれて msgid "This file is invalid and cannot be played." msgstr "このファイルは不正なため再生することができません" -msgid "This file is incomplete and cannot be played." -msgstr "このファイルは正常ではないため、再生することができません" - msgid "This file is corrupt and cannot be played." msgstr "このファイルは壊れているため再生することができません" +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "このファイルは正常ではないため、再生することができません" + msgid "The video in this file might not play correctly." msgstr "このファイルの中の動画は正しく再生できないかもしれません" @@ -697,6 +700,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "" +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"デバイス '%s' 上の現在の入力の取得に失敗しました。ラジオデバイスかもしれませ" +"ん" + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "デバイス '%2$s' 上のチューナー %1$d の設定の取得に失敗しました。" + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "" diff --git a/po/lt.po b/po/lt.po index 2a213c69b0..29d9afb4ca 100644 --- a/po/lt.po +++ b/po/lt.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.23.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-07-16 19:34+0300\n" "Last-Translator: Žygimantas Beručka \n" "Language-Team: Lithuanian \n" @@ -72,12 +72,15 @@ msgstr "Šiame faile nėra atkurtinų srautų." msgid "This file is invalid and cannot be played." msgstr "Failas netinkamas ir negali būti atkurtas." -msgid "This file is incomplete and cannot be played." -msgstr "Failas nebaigtas ir negali būti atkurtas." - msgid "This file is corrupt and cannot be played." msgstr "Failas sugadintas ir negali būti atkurtas." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Failas nebaigtas ir negali būti atkurtas." + msgid "The video in this file might not play correctly." msgstr "Šiame faile esantis vaizdo įrašas gali būti atkurtas nekorektiškai." @@ -624,6 +627,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Nepavyko nustatyti įvesties %d įrenginyje %s." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Nepavyko gauti dabartinės įvesties įrenginyje „%s“. Galbūt tai radijo " +"įrenginys" + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Nepavyko nustatyti įvesties %d įrenginyje %s." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Nepavyko sustatyti į eilė buferių įrenginyje „%s“." diff --git a/po/lv.po b/po/lv.po index 1b3f6916c3..f2ee50cca1 100644 --- a/po/lv.po +++ b/po/lv.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.23.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-07-07 11:53+0100\n" "Last-Translator: Rihards Priedītis \n" "Language-Team: Latvian \n" @@ -72,12 +72,15 @@ msgstr "Šis fails nesatur nevienu atskaņojamu straumi." msgid "This file is invalid and cannot be played." msgstr "Šis fails ir nederīgs un nevar tikt atskaņots." -msgid "This file is incomplete and cannot be played." -msgstr "Šis pails ir nepabeigts un nevar tikt atskaņots." - msgid "This file is corrupt and cannot be played." msgstr "Šis fails ir bojāts un nevar tikt atskaņots." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Šis pails ir nepabeigts un nevar tikt atskaņots." + msgid "The video in this file might not play correctly." msgstr "Video šajā failā var tikt atskaņots nepareizi." @@ -626,6 +629,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Neizdevās uzstādīt ievadi %d uz ierīces %s." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Neizdevās saņemt pašreizējo ievadi no ierīces \"%s\". Iespējams tā ir radio " +"ierīce" + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Neizdevās uzstādīt ievadi %d uz ierīces %s." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Nevar ierindod buferus ierīcē \"%s\"." diff --git a/po/mt.po b/po/mt.po index b8ab7c36bd..9b847571a3 100644 --- a/po/mt.po +++ b/po/mt.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good-0.10.10.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2008-10-26 19:09+0100\n" "Last-Translator: Michel Bugeja \n" "Language-Team: Maltese \n" @@ -70,12 +70,15 @@ msgstr "Il-fajl ma fiħ l-ebda stream li tista tindaqq." msgid "This file is invalid and cannot be played." msgstr "Il-fajl mhux validu u ma jistax jindaqq" -msgid "This file is incomplete and cannot be played." -msgstr "Il-fajl mhux komplut u ma jistax jindaqq," - msgid "This file is corrupt and cannot be played." msgstr "Il-fajl huwa korrott u ma jistax jinfetaħ." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Il-fajl mhux komplut u ma jistax jindaqq," + msgid "The video in this file might not play correctly." msgstr "Jista jkun illi l-vidjo ġo dan il-fajl ma jidhirx sewwa." @@ -658,6 +661,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Problema biex nissettja input %d fuq apparat %s." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Problema biex inġib current input fuq apparat '%s'. Jista jkun li huwa " +"apparat tar-radju." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Problema biex nissettja input %d fuq apparat %s." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Could not enqueue buffers in device '%s'." diff --git a/po/nb.po b/po/nb.po index fd4cb7bbf8..f37e3b0b0a 100644 --- a/po/nb.po +++ b/po/nb.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.25.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-01 17:27+0000\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-10-24 21:53+0200\n" "Last-Translator: Kjartan Maraas \n" "Language-Team: Norwegian Bokmaal \n" @@ -66,10 +66,13 @@ msgstr "" msgid "This file is invalid and cannot be played." msgstr "" -msgid "This file is incomplete and cannot be played." +msgid "This file is corrupt and cannot be played." msgstr "" -msgid "This file is corrupt and cannot be played." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." msgstr "" msgid "The video in this file might not play correctly." @@ -602,6 +605,15 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "" +#, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Kunne ikke lukke VFS-fil «%s»." + #, fuzzy, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Kunne ikke lukke VFS-fil «%s»." diff --git a/po/nl.po b/po/nl.po index 6c3939c899..5f6f242e56 100644 --- a/po/nl.po +++ b/po/nl.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.25.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-01 17:27+0000\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-10-25 20:15+0200\n" "Last-Translator: Freek de Kruijf \n" "Language-Team: Dutch \n" @@ -71,12 +71,15 @@ msgstr "Dit bestand bevat geen afspeelbare stromen." msgid "This file is invalid and cannot be played." msgstr "Dit bestand is ongeldig en kan niet afgespeeld worden." -msgid "This file is incomplete and cannot be played." -msgstr "Dit bestand is incompleet en kan niet afgespeeld worden." - msgid "This file is corrupt and cannot be played." msgstr "Dit bestand is beschadigd en kan niet afgespeeld worden." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Dit bestand is incompleet en kan niet afgespeeld worden." + msgid "The video in this file might not play correctly." msgstr "De video in dit bestand zal mogelijk niet correct afgespeeld worden." @@ -627,6 +630,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Kan invoer %d op apparaat '%s' niet instellen." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Kan de huidige instellingen van apparaat '%s' niet verkrijgen. Het is " +"mogelijk een radio" + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Kan invoer %d op apparaat '%s' niet instellen." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Kan geen buffers toekennen in apparaat '%s'." diff --git a/po/or.po b/po/or.po index 0f4ebbc6bb..f91b2daf39 100644 --- a/po/or.po +++ b/po/or.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-0.8.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2004-09-27 13:32+0530\n" "Last-Translator: Gora Mohanty \n" "Language-Team: Oriya \n" @@ -72,10 +72,13 @@ msgstr "" msgid "This file is invalid and cannot be played." msgstr "" -msgid "This file is incomplete and cannot be played." +msgid "This file is corrupt and cannot be played." msgstr "" -msgid "This file is corrupt and cannot be played." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." msgstr "" msgid "The video in this file might not play correctly." @@ -628,6 +631,15 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "\"%s\" ଧ୍ବନି ଯନ୍ତ୍ର ବନ୍ଦ କରିହେଲା ନାହିଁ." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "\"%s\" ଯନ୍ତ୍ରରୁ ପର୍ଯ୍ଯାପ୍ତ ଅସ୍ଥାୟୀ ସଞ୍ଚୟ ସ୍ଥାନ ଆଣିହେଲା ନାହିଁ." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "\"%s\" ଧ୍ବନି ଯନ୍ତ୍ର ବନ୍ଦ କରିହେଲା ନାହିଁ." + #, fuzzy, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "\"%s\" ଯନ୍ତ୍ରରୁ ଅସ୍ଥାୟୀ ସଞ୍ଚୟ ସ୍ଥାନ ଆଣିହେଲା ନାହିଁ." diff --git a/po/pl.po b/po/pl.po index 59347df17f..2ba0bc1384 100644 --- a/po/pl.po +++ b/po/pl.po @@ -1,13 +1,13 @@ # Polish translation for gst-plugins-good. # This file is distributed under the same license as the gst-plugins-good package. -# Jakub Bogusz , 2007-2010. +# Jakub Bogusz , 2007-2011. # msgid "" msgstr "" -"Project-Id-Version: gst-plugins-good 0.10.25.3\n" +"Project-Id-Version: gst-plugins-good 0.10.26.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-01 17:27+0000\n" -"PO-Revision-Date: 2010-10-25 07:27+0200\n" +"POT-Creation-Date: 2011-01-11 19:32+0000\n" +"PO-Revision-Date: 2011-01-07 21:53+0100\n" "Last-Translator: Jakub Bogusz \n" "Language-Team: Polish \n" "Language: pl\n" @@ -66,12 +66,15 @@ msgstr "Ten plik nie zawiera strumieni nadających się do odtworzenia." msgid "This file is invalid and cannot be played." msgstr "Ten plik jest błędny i nie może być odtworzony." -msgid "This file is incomplete and cannot be played." -msgstr "Ten plik jest niekompletny i nie może być odtworzony." - msgid "This file is corrupt and cannot be played." msgstr "Ten plik jest uszkodzony i nie może być odtworzony." +msgid "Invalid atom size." +msgstr "Błędny rozmiar atomu." + +msgid "This file is incomplete and cannot be played." +msgstr "Ten plik jest niekompletny i nie może być odtworzony." + msgid "The video in this file might not play correctly." msgstr "Obraz w tym pliku może nie być odtwarzany prawidłowo." @@ -622,6 +625,16 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Nie udało się ustawić wejścia %d urządzenia %s." +#, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Nie udało się uzyskać aktualnego wyjścia urządzenia '%s'. Może to radio" + +#, c-format +msgid "Failed to set output %d on device %s." +msgstr "Nie udało się ustawić wyjścia %d urządzenia %s." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Nie udało się skolejkować buforów urządzenia '%s'." diff --git a/po/pt_BR.po b/po/pt_BR.po index 5a80f5c25b..6a669ff87c 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -1,7 +1,7 @@ # Brazilian Portuguese translation of gst-plugins-good. # This file is distributed under the same license as the gst-plugins-good package. -# Copyright (C) 2008-2010 Free Software Foundation, Inc. -# Fabrício Godoy , 2008-2010. +# Copyright (C) 2008-2011 Free Software Foundation, Inc. +# Fabrício Godoy , 2008-2011. # # data flow -> fluxo de dados # streaming -> fluxo contínuo @@ -9,10 +9,10 @@ # msgid "" msgstr "" -"Project-Id-Version: gst-plugins-good 0.10.23.2\n" +"Project-Id-Version: gst-plugins-good 0.10.26.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" -"PO-Revision-Date: 2010-06-28 21:00-0300\n" +"POT-Creation-Date: 2011-01-11 19:32+0000\n" +"PO-Revision-Date: 2011-01-08 01:28-0300\n" "Last-Translator: Fabrício Godoy \n" "Language-Team: Brazilian Portuguese \n" "Language: pt_BR\n" @@ -41,30 +41,28 @@ msgstr "Falha ao decodificar a imagem JPEG" msgid "Could not connect to server" msgstr "Não foi possível conectar ao servidor" -#, fuzzy msgid "Server does not support seeking." -msgstr "Não há suporte a captura de vídeo no dispositivo \"%s\"" +msgstr "Nâo há suporte a busca pelo servidor." -#, fuzzy msgid "Could not resolve server name." -msgstr "Não foi possível conectar ao servidor" +msgstr "Não foi possível resolver o nome do servidor." -#, fuzzy msgid "Could not establish connection to server." -msgstr "Não foi possível estabelecer uma conexão com servidor de som" +msgstr "Não foi possível estabelecer uma conexão com servidor." msgid "Secure connection setup failed." -msgstr "" +msgstr "Configuração de conexão segura falhou." msgid "" "A network error occured, or the server closed the connection unexpectedly." msgstr "" +"Um erro de rede ocorreu, ou o servidor fechou a conexão inesperadamente." msgid "Server sent bad data." -msgstr "" +msgstr "O servidor enviou dados ruins." msgid "No URL set." -msgstr "" +msgstr "Nenhum URL definido." msgid "No or invalid input audio, AVI stream will be corrupt." msgstr "Entrada de áudio nula ou inválida, o fluxo AVI pode estar corrompido." @@ -75,12 +73,15 @@ msgstr "Este arquivo não contêm fluxos reproduzíveis." msgid "This file is invalid and cannot be played." msgstr "Este arquivo é inválido e não pôde ser reproduzido." -msgid "This file is incomplete and cannot be played." -msgstr "Este arquivo está incompleto e não pôde ser reproduzido." - msgid "This file is corrupt and cannot be played." msgstr "Este arquivo está corrompido e não pôde ser reproduzido." +msgid "Invalid atom size." +msgstr "Tamanho de Atom inválido." + +msgid "This file is incomplete and cannot be played." +msgstr "Este arquivo está incompleto e não pôde ser reproduzido." + msgid "The video in this file might not play correctly." msgstr "O vídeo neste arquivo pode não ser reproduzido corretamente." @@ -634,6 +635,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Falha ao definir a entrada %d no dispositivo %s." +#, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Falha ao obter a saída atual no dispositivo \"%s\". Talvez seja um " +"dispositivo de rádio" + +#, c-format +msgid "Failed to set output %d on device %s." +msgstr "Falha ao definir a saída %d no dispositivo %s." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Não possível adicionar buffers à fila no dispositivo \"%s\"." @@ -670,58 +682,3 @@ msgstr "Ainda não há suporte a mudança de resolução enquanto está executan msgid "Cannot operate without a clock" msgstr "Não é possível operar sem um temporizador" - -#~ msgid "Failed to enumerate possible video formats device '%s' can work with" -#~ msgstr "" -#~ "Falha ao especificar os formatos possíveis de vídeo que o dispositivo \"%s" -#~ "\" pode funcionar" - -#~ msgid "" -#~ "The buffer type is not supported, or the index is out of bounds, or no " -#~ "buffers have been allocated yet, or the userptr or length are invalid. " -#~ "device %s" -#~ msgstr "" -#~ "Não há suporte ao tipo de buffer, ou o índice está fora dos limites, ou " -#~ "nenhum buffer foi definido ainda, ou o userptr ou tamanho são inválidos. " -#~ "Dispositivo %s" - -#~ msgid "" -#~ "Failed trying to get video frames from device '%s'. Not enough memory." -#~ msgstr "" -#~ "Falha ao tentar obter os quadros de vídeo do dispositivo \"%s\". Memória " -#~ "insuficiente." - -#~ msgid "insufficient memory to enqueue a user pointer buffer. device %s." -#~ msgstr "" -#~ "memória insuficiente para adicionar à fila um buffer de ponteiro do " -#~ "usuário. Dispositivo %s." - -#~ msgid "No free buffers found in the pool at index %d." -#~ msgstr "Nenhum buffer livre encontrado no reservatório, no índice %d." - -#~ msgid "Device '%s' cannot capture at %dx%d" -#~ msgstr "O dispositivo \"%s\" não captura à %dx%d" - -#~ msgid "Device '%s' cannot capture in the specified format" -#~ msgstr "O dispositivo \"%s\" não captura no formato especificado" - -#~ msgid "Could not get buffers from device '%s'." -#~ msgstr "Não foi possível obter buffers do dispositivo \"%s\"." - -#~ msgid "Could not get enough buffers from device '%s'." -#~ msgstr "Não foi possível obter buffers suficientes do dispositivo \"%s\"." - -#~ msgid "Error starting streaming capture from device '%s'." -#~ msgstr "Erro ao iniciar um fluxo contínuo de captura do dispositivo \"%s\"." - -#~ msgid "Error stopping streaming capture from device '%s'." -#~ msgstr "Erro ao parar um fluxo contínuo de captura do dispositivo \"%s\"." - -#~ msgid "Failed getting controls attributes on device '%s.'" -#~ msgstr "Falha ao obter atributos de controle no dispositivo \"%s\"." - -#~ msgid "Could not read from CD." -#~ msgstr "Não foi possível ler o CD." - -#~ msgid "Disc is not an Audio CD." -#~ msgstr "O disco não é um CD de áudio." diff --git a/po/ro.po b/po/ro.po index 896616e532..97ac758708 100644 --- a/po/ro.po +++ b/po/ro.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.23.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-08-16 03:22+0300\n" "Last-Translator: Lucian Adrian Grijincu \n" "Language-Team: Romanian \n" @@ -72,12 +72,15 @@ msgstr "Acest fișier nu conține fluxuri ce pot fi redate." msgid "This file is invalid and cannot be played." msgstr "Fișierul nu este valid și nu poate fi redat." -msgid "This file is incomplete and cannot be played." -msgstr "Acest fișier nu este complet și nu poate fi redat." - msgid "This file is corrupt and cannot be played." msgstr "Acest fișier este corupt și nu poate fi redat." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Acest fișier nu este complet și nu poate fi redat." + msgid "The video in this file might not play correctly." msgstr "Este posibil ca fișierul video să nu fie redat corect." @@ -633,6 +636,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Definirea valorii %d pentru dispozitivul „%s” a eșuat." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Obținerea intrări curente pentru dispozitivul „%s” a eșuat. Posibil să fie " +"un dispozitiv radio." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Definirea valorii %d pentru dispozitivul „%s” a eșuat." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Nu s-a putut programa memoria tampon în dispozitivul „%s”." diff --git a/po/ru.po b/po/ru.po index 28bf04077d..0428d5fe67 100644 --- a/po/ru.po +++ b/po/ru.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.13.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2009-02-12 14:35+0200\n" "Last-Translator: Pavel Maryanov \n" "Language-Team: Russian \n" @@ -72,12 +72,15 @@ msgstr "Файл не содержит воспроизводимых поток msgid "This file is invalid and cannot be played." msgstr "Файл некорректен и не может быть воспроизведён." -msgid "This file is incomplete and cannot be played." -msgstr "Файл не полон и не может быть воспроизведён." - msgid "This file is corrupt and cannot be played." msgstr "Файл повреждён и не может быть воспроизведён." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Файл не полон и не может быть воспроизведён." + msgid "The video in this file might not play correctly." msgstr "Видео из этого файла может быть воспроизведено некорректно." @@ -663,6 +666,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Не удалось выбрать вход %d для устройства %s" +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Не удалось определить активный вход устройства «%s». Возможно, это радио-" +"устройство" + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Не удалось выбрать вход %d для устройства %s" + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Не удалось добавить в очередь буферы устройства «%s»" diff --git a/po/sk.po b/po/sk.po index b9048a1e1d..c1304b08ab 100644 --- a/po/sk.po +++ b/po/sk.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.25.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-18 00:43+0000\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-11-08 15:48+0100\n" "Last-Translator: Peter Tuhársky \n" "Language-Team: Slovak \n" @@ -69,12 +69,15 @@ msgstr "Tento súbor neobsahuje žiadne prehrateľné prúdy údajov." msgid "This file is invalid and cannot be played." msgstr "Tento súbor je chybný a nedá sa prehrať." -msgid "This file is incomplete and cannot be played." -msgstr "Tento súbor je neúplný a nedá sa prehrať." - msgid "This file is corrupt and cannot be played." msgstr "Tento súbor je poškodený a nedá sa prehrať." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Tento súbor je neúplný a nedá sa prehrať." + msgid "The video in this file might not play correctly." msgstr "Video v tomto súbore možno nebude hrať korektne." @@ -626,6 +629,16 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Nepodarilo sa nastaviť vstup %d na zariadení %s." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Nepodarilo sa zistiť súčasný vstup na zariadení '%s'. Možno je to rádio." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Nepodarilo sa nastaviť vstup %d na zariadení %s." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Nepodarilo sa zaradiť vyrovnávaciu pamäť na zariadení '%s'." diff --git a/po/sl.po b/po/sl.po index c07e0af215..cb587ace1d 100644 --- a/po/sl.po +++ b/po/sl.po @@ -2,15 +2,15 @@ # This file is distributed under the same license as the gst-plugins-good package. # Copyright (C) 2005 Free Software Foundation, Inc. # -# Matej Urbančič , 2010. # Robert Horvat , 2010. +# Matej Urbančič , 2010 - 2011. # msgid "" msgstr "" -"Project-Id-Version: gst-plugins-good 0.10.25.3\n" +"Project-Id-Version: gst-plugins-good 0.10.26.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-01 17:27+0000\n" -"PO-Revision-Date: 2010-10-25 12:23+0100\n" +"POT-Creation-Date: 2011-01-11 19:32+0000\n" +"PO-Revision-Date: 2011-01-07 20:28+0100\n" "Last-Translator: Matej Urbančič \n" "Language-Team: Slovenian \n" "Language: sl\n" @@ -64,7 +64,7 @@ msgid "Server sent bad data." msgstr "Strežnik je poslal slabe podatke." msgid "No URL set." -msgstr "Ni nastavljen naslov URL." +msgstr "Ni nastavljenega naslova URL." msgid "No or invalid input audio, AVI stream will be corrupt." msgstr "Napaka vhodnega zvoka, AVI pretok bo pokvarjen." @@ -75,18 +75,21 @@ msgstr "Datoteka ne vsebuje pretokov za predvajanje." msgid "This file is invalid and cannot be played." msgstr "Datoteka je napačna, zato je ni mogoče predvajati." -msgid "This file is incomplete and cannot be played." -msgstr "Datoteka ni popolna, zato je ni mogoče predvajati." - msgid "This file is corrupt and cannot be played." msgstr "Datoteka je pokvarjena, zato je ni mogoče predvajati." +msgid "Invalid atom size." +msgstr "Neveljavna velikost atoma" + +msgid "This file is incomplete and cannot be played." +msgstr "Datoteka ni popolna, zato je ni mogoče predvajati." + msgid "The video in this file might not play correctly." msgstr "Video zapis v tej datoteki morda ne bo predvajan pravilno." #, c-format msgid "This file contains too many streams. Only playing first %d" -msgstr "Datoteka vsebuje več pretokov. Predvajanje le prvih %d" +msgstr "Datoteka vsebuje več pretokov. Predvajano bo le začetnih %d" msgid "" "No supported stream was found. You might need to install a GStreamer RTSP " @@ -631,6 +634,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Napaka med določanjem vhoda %d naprave %s." +#, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Napaka med pridobivanjem odvodnega signala naprave '%s'. Morda je radijska " +"naprava." + +#, c-format +msgid "Failed to set output %d on device %s." +msgstr "Napaka med določanjem odvoda %d naprave %s." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Ni mogoče uvrstiti medpomnilnika naprave '%s'." @@ -665,7 +679,7 @@ msgid "Changing resolution at runtime is not yet supported." msgstr "Spreminjanje ločljivosti med delovanjem še ni podprto." msgid "Cannot operate without a clock" -msgstr "Ni mogoče izvajati opravil brez ure" +msgstr "Izvajanje opravil brez ure ni mogoče" #~ msgid "Describes the selected input element." #~ msgstr "Opisuje izbran vnosni predmet." diff --git a/po/sq.po b/po/sq.po index 9c53eda84b..014e9cbb82 100644 --- a/po/sq.po +++ b/po/sq.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins 0.8.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2004-08-07 20:29+0200\n" "Last-Translator: Laurent Dhima \n" "Language-Team: Albanian \n" @@ -70,10 +70,13 @@ msgstr "" msgid "This file is invalid and cannot be played." msgstr "" -msgid "This file is incomplete and cannot be played." +msgid "This file is corrupt and cannot be played." msgstr "" -msgid "This file is corrupt and cannot be played." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." msgstr "" msgid "The video in this file might not play correctly." @@ -626,6 +629,15 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "E pamundur mbyllja e dispozitivit audio \"%s\"." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "E pamundur marrja e buffers të mjaftueshëm nga dispozitivi \"%s\"." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "E pamundur mbyllja e dispozitivit audio \"%s\"." + #, fuzzy, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "E pamundur marrja e buffers nga dispozitivi \"%s\"." diff --git a/po/sr.po b/po/sr.po index 81f69de103..528ffe5b24 100644 --- a/po/sr.po +++ b/po/sr.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins 0.7.6\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2004-03-13 00:18+0100\n" "Last-Translator: Danilo Segan \n" "Language-Team: Serbian \n" @@ -71,10 +71,13 @@ msgstr "" msgid "This file is invalid and cannot be played." msgstr "" -msgid "This file is incomplete and cannot be played." +msgid "This file is corrupt and cannot be played." msgstr "" -msgid "This file is corrupt and cannot be played." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." msgstr "" msgid "The video in this file might not play correctly." @@ -627,6 +630,15 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Не могу да отворим радио уређај '%s'" +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "Не могу да примим довољно бафера са уређаја „%s“." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Не могу да отворим радио уређај '%s'" + #, fuzzy, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Не могу да примим бафере са уређаја „%s“." diff --git a/po/sv.po b/po/sv.po index 99f25c7ade..8f4509afd2 100644 --- a/po/sv.po +++ b/po/sv.po @@ -1,15 +1,15 @@ # Swedish messages for gst-plugins-good. -# Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. +# Copyright (C) 2007-2011 Free Software Foundation, Inc. # This file is distributed under the same license as the gst-plugins-good package. # Christian Rose , 2004. -# Daniel Nylander , 2007, 2008, 2009, 2010. +# Daniel Nylander , 2007, 2008, 2009, 2010, 2011. # msgid "" msgstr "" -"Project-Id-Version: gst-plugins-good 0.10.25.3\n" +"Project-Id-Version: gst-plugins-good 0.10.26.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-01 17:27+0000\n" -"PO-Revision-Date: 2010-10-26 08:50+0100\n" +"POT-Creation-Date: 2011-01-11 19:32+0000\n" +"PO-Revision-Date: 2011-01-09 19:36+0100\n" "Last-Translator: Daniel Nylander \n" "Language-Team: Swedish \n" "Language: sv\n" @@ -68,12 +68,15 @@ msgstr "Den här filen innehåller inga uppspelningsbara strömmar." msgid "This file is invalid and cannot be played." msgstr "Den här filen är ogiltig och kan inte spelas upp." -msgid "This file is incomplete and cannot be played." -msgstr "Den här filen är inte fullständig och kan inte spelas upp." - msgid "This file is corrupt and cannot be played." msgstr "Den här filen är skadad och kan inte spelas upp." +msgid "Invalid atom size." +msgstr "Ogiltig atomstorlek." + +msgid "This file is incomplete and cannot be played." +msgstr "Den här filen är inte fullständig och kan inte spelas upp." + msgid "The video in this file might not play correctly." msgstr "Videon i den här filen kanske inte kan spelas upp korrekt." @@ -630,6 +633,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Kunde inte ställa in ingång %d på enheten %s." +#, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Misslyckades med att få tag på aktuell utgång på enheten \"%s\". Kan vara en " +"radioenhet" + +#, c-format +msgid "Failed to set output %d on device %s." +msgstr "Misslyckades med att ställa in utgång %d på enheten %s." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Kunde inte kölägga buffertar i enheten \"%s\"." @@ -1486,9 +1500,6 @@ msgstr "Kan inte fungera utan en klocka" #~ msgid "_Set device" #~ msgstr "_Ställ in enhet" -#~ msgid "Invalid CD device" -#~ msgstr "Ogiltig cd-enhet" - #~ msgid "Volume control" #~ msgstr "Volymkontroll" diff --git a/po/tr.po b/po/tr.po index d780abdfbd..c0823489e2 100644 --- a/po/tr.po +++ b/po/tr.po @@ -1,12 +1,13 @@ -# translation of gst-plugins-good-0.10.25.3.po to Turkish +# translation of gst-plugins-good-0.10.26.2.po to Turkish # This file is put in the public domain. # Server Acim , 2010. +# Server Acim , 2011. msgid "" msgstr "" -"Project-Id-Version: gst-plugins-good 0.10.25.3\n" +"Project-Id-Version: gst-plugins-good 0.10.26.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-18 00:43+0000\n" -"PO-Revision-Date: 2010-11-16 16:41+0200\n" +"POT-Creation-Date: 2011-01-11 19:32+0000\n" +"PO-Revision-Date: 2011-01-08 00:03+0200\n" "Last-Translator: Server Acim \n" "Language-Team: Turkish \n" "Language: tr\n" @@ -67,12 +68,15 @@ msgstr "Dosya çalınabilir akışlar içermiyor." msgid "This file is invalid and cannot be played." msgstr "Bu dosya geçersiz ve oynatılamaz." -msgid "This file is incomplete and cannot be played." -msgstr "Dosya eksik ve oynatılamaz." - msgid "This file is corrupt and cannot be played." msgstr "Bu dosya bozuk ve oynatılamaz." +msgid "Invalid atom size." +msgstr "Geçersiz atom boyutu" + +msgid "This file is incomplete and cannot be played." +msgstr "Dosya eksik ve oynatılamaz." + msgid "The video in this file might not play correctly." msgstr "Bu dosyadaki vidyo doğru oynatılamıyabilir." @@ -615,6 +619,16 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Giriş değeri olarak bu %d şu aygıtta %s ayarlanamadı." +#, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Şu aygıtta '%s' geçerli giriş elde edilemedi. O bir radyo aygıtı olabilir." + +#, c-format +msgid "Failed to set output %d on device %s." +msgstr "Çıkış değeri olarak %d şu aygıtta %s elde edilemedi." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Aygıtta '%s' arabellek kuyruğa sokulamıyor." diff --git a/po/uk.po b/po/uk.po index 6c54efa4c6..34eafceb87 100644 --- a/po/uk.po +++ b/po/uk.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.6\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2007-07-05 15:40+0200\n" "Last-Translator: Maxim V. Dziumanenko \n" "Language-Team: Ukrainian \n" @@ -71,12 +71,15 @@ msgstr "Файл містить потоки, які неможливо відт msgid "This file is invalid and cannot be played." msgstr "Файл неповний або не може відтворюватись." -msgid "This file is incomplete and cannot be played." -msgstr "Файл неповний або не може відтворюватись." - msgid "This file is corrupt and cannot be played." msgstr "Файл пошкоджений та не може бути відтворений." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Файл неповний або не може відтворюватись." + msgid "The video in this file might not play correctly." msgstr "Потік відео у цьому файлі неможливо коректно відтворити." @@ -616,6 +619,17 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Не вдається встановити ввід %d пристрою \"%s\"." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Не вдається отримати поточний ввід пристрою \"%s\". Можливо цей пристрій - " +"радіо." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Не вдається встановити ввід %d пристрою \"%s\"." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Не вдається опитати буфери від пристрою \"%s\"." diff --git a/po/vi.po b/po/vi.po index bb2a94d0ba..d3d7f5e41a 100644 --- a/po/vi.po +++ b/po/vi.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.23.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-10-16 01:23+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2010-10-03 18:48+1030\n" "Last-Translator: Clytie Siddall \n" "Language-Team: Vietnamese \n" @@ -73,12 +73,15 @@ msgstr "Tập tin này không chứa luồng có thể phát." msgid "This file is invalid and cannot be played." msgstr "Tập tin này không hợp lệ nên không thể phát được." -msgid "This file is incomplete and cannot be played." -msgstr "Tập tin này chưa hoàn thành nên không thể được phát." - msgid "This file is corrupt and cannot be played." msgstr "Tập tin này bị hỏng nên không thể phát." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "Tập tin này chưa hoàn thành nên không thể được phát." + msgid "The video in this file might not play correctly." msgstr "Ảnh động trong tập tin này có thể không phát đúng." @@ -626,6 +629,16 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "Lỗi đặt dữ liệu nhập %d vào thiết bị « %s »." +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" +"Lỗi lấy kết nhập hiện thời vào thiết bị « %s ». Có thể là thiết bị thu thanh." + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "Lỗi đặt dữ liệu nhập %d vào thiết bị « %s »." + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "Không thể phụ thêm các bộ đệm vào hàng đợi trên thiết bị « %s »." diff --git a/po/zh_CN.po b/po/zh_CN.po index 154344a167..20354bb49f 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good 0.10.16.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2009-11-13 22:20+0800\n" "Last-Translator: Ji ZhengYu \n" "Language-Team: Chinese (simplified) \n" @@ -70,12 +70,15 @@ msgstr "此文件不包含可播放的流。" msgid "This file is invalid and cannot be played." msgstr "此文件无效,无法播放。" -msgid "This file is incomplete and cannot be played." -msgstr "此文件不完整且无法播放。" - msgid "This file is corrupt and cannot be played." msgstr "此文件已损坏,无法播放。" +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." +msgstr "此文件不完整且无法播放。" + msgid "The video in this file might not play correctly." msgstr "此文件中的视频可能无法正确播放。" @@ -633,6 +636,15 @@ msgstr "获取设备‘%s’上的当前输入出错。也许它是一个广播 msgid "Failed to set input %d on device %s." msgstr "设置设备 %2$s 上的输入 %1$d 时出错。" +#, fuzzy, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "获取设备‘%s’上的当前输入出错。也许它是一个广播设备" + +#, fuzzy, c-format +msgid "Failed to set output %d on device %s." +msgstr "设置设备 %2$s 上的输入 %1$d 时出错。" + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "无法对设备‘%s’中的缓冲区进行排序。" diff --git a/po/zh_HK.po b/po/zh_HK.po index 2d3c2400a6..1049ff8182 100644 --- a/po/zh_HK.po +++ b/po/zh_HK.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good-0.10.2 0.10.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2006-08-29 01:08+0800\n" "Last-Translator: Abel Cheung \n" "Language-Team: Chinese (Hong Kong) \n" @@ -66,10 +66,13 @@ msgstr "" msgid "This file is invalid and cannot be played." msgstr "" -msgid "This file is incomplete and cannot be played." +msgid "This file is corrupt and cannot be played." msgstr "" -msgid "This file is corrupt and cannot be played." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." msgstr "" msgid "The video in this file might not play correctly." @@ -623,6 +626,15 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "" +#, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" + +#, c-format +msgid "Failed to set output %d on device %s." +msgstr "" + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "" diff --git a/po/zh_TW.po b/po/zh_TW.po index 7c944e3042..eb1b3534ea 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: gst-plugins-good-0.10.2 0.10.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-09-12 00:08+0100\n" +"POT-Creation-Date: 2011-01-07 01:15+0000\n" "PO-Revision-Date: 2006-08-29 01:08+0800\n" "Last-Translator: Abel Cheung \n" "Language-Team: Chinese (traditional) \n" @@ -66,10 +66,13 @@ msgstr "" msgid "This file is invalid and cannot be played." msgstr "" -msgid "This file is incomplete and cannot be played." +msgid "This file is corrupt and cannot be played." msgstr "" -msgid "This file is corrupt and cannot be played." +msgid "Invalid atom size." +msgstr "" + +msgid "This file is incomplete and cannot be played." msgstr "" msgid "The video in this file might not play correctly." @@ -623,6 +626,15 @@ msgstr "" msgid "Failed to set input %d on device %s." msgstr "" +#, c-format +msgid "" +"Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" + +#, c-format +msgid "Failed to set output %d on device %s." +msgstr "" + #, c-format msgid "Could not enqueue buffers in device '%s'." msgstr "" diff --git a/sys/directsound/gstdirectsoundsink.c b/sys/directsound/gstdirectsoundsink.c index e64c8ac38e..462c41210f 100644 --- a/sys/directsound/gstdirectsoundsink.c +++ b/sys/directsound/gstdirectsoundsink.c @@ -56,6 +56,13 @@ #include +#ifdef __CYGWIN__ +#include +#ifndef _swab +#define _swab swab +#endif +#endif + GST_DEBUG_CATEGORY_STATIC (directsoundsink_debug); #define GST_CAT_DEFAULT directsoundsink_debug diff --git a/sys/v4l2/Makefile.am b/sys/v4l2/Makefile.am index 8d46d438d9..ab996a15a8 100644 --- a/sys/v4l2/Makefile.am +++ b/sys/v4l2/Makefile.am @@ -1,26 +1,28 @@ plugin_LTLIBRARIES = libgstvideo4linux2.la -# overlay is still not supported in current implementation -# if USE_XVIDEO -#xv_source = gstv4l2xoverlay.c -#xv_libs = $(X_LIBS) $(XVIDEO_LIBS) -#else -#xv_source = -#xv_libs = -#endif +if USE_XVIDEO +xv_source = gstv4l2xoverlay.c +xv_libs = $(X_LIBS) $(XVIDEO_LIBS) +else +xv_source = +xv_libs = +endif libgstvideo4linux2_la_SOURCES = gstv4l2.c \ gstv4l2colorbalance.c \ gstv4l2object.c \ gstv4l2bufferpool.c \ gstv4l2src.c \ - gstv4l2sink.c \ gstv4l2tuner.c \ gstv4l2vidorient.c \ v4l2_calls.c \ v4l2src_calls.c \ $(xv_source) +if BUILD_EXPERIMENTAL +libgstvideo4linux2_la_SOURCES += gstv4l2sink.c +endif + libgstvideo4linux2_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) \ $(GST_CONTROLLER_CFLAGS) \ diff --git a/sys/v4l2/gstv4l2.c b/sys/v4l2/gstv4l2.c index 2599f77286..4a7056fd52 100644 --- a/sys/v4l2/gstv4l2.c +++ b/sys/v4l2/gstv4l2.c @@ -32,7 +32,9 @@ #include "gstv4l2object.h" #include "gstv4l2src.h" +#ifdef HAVE_EXPERIMENTAL #include "gstv4l2sink.h" +#endif /* #include "gstv4l2jpegsrc.h" */ /* #include "gstv4l2mjpegsrc.h" */ /* #include "gstv4l2mjpegsink.h" */ @@ -52,8 +54,10 @@ plugin_init (GstPlugin * plugin) if (!gst_element_register (plugin, "v4l2src", GST_RANK_PRIMARY, GST_TYPE_V4L2SRC) || +#ifdef HAVE_EXPERIMENTAL !gst_element_register (plugin, "v4l2sink", GST_RANK_NONE, GST_TYPE_V4L2SINK) || +#endif /* !gst_element_register (plugin, "v4l2jpegsrc", */ /* GST_RANK_NONE, GST_TYPE_V4L2JPEGSRC) || */ /* !gst_element_register (plugin, "v4l2mjpegsrc", */ diff --git a/sys/v4l2/gstv4l2bufferpool.c b/sys/v4l2/gstv4l2bufferpool.c index b8c61bac56..e976ec5079 100644 --- a/sys/v4l2/gstv4l2bufferpool.c +++ b/sys/v4l2/gstv4l2bufferpool.c @@ -34,10 +34,20 @@ #include #include "gstv4l2src.h" +#ifdef HAVE_EXPERIMENTAL #include "gstv4l2sink.h" +#endif #include "v4l2_calls.h" #include "gst/gst-i18n-plugin.h" +/* videodev2.h is not versioned and we can't easily check for the presence + * of enum values at compile time, but the V4L2_CAP_VIDEO_OUTPUT_OVERLAY define + * was added in the same commit as V4L2_FIELD_INTERLACED_{TB,BT} (b2787845) */ +#ifndef V4L2_CAP_VIDEO_OUTPUT_OVERLAY +#define V4L2_FIELD_INTERLACED_TB 8 +#define V4L2_FIELD_INTERLACED_BT 9 +#endif + GST_DEBUG_CATEGORY_EXTERN (v4l2_debug); #define GST_CAT_DEFAULT v4l2_debug @@ -263,7 +273,7 @@ gst_v4l2_buffer_pool_get_type (void) if (G_UNLIKELY (_gst_v4l2_buffer_pool_type == 0)) { static const GTypeInfo v4l2_buffer_pool_info = { - sizeof (GstBufferClass), + sizeof (GstMiniObjectClass), NULL, NULL, gst_v4l2_buffer_pool_class_init, @@ -290,8 +300,10 @@ get_v4l2_object (GstElement * v4l2elem) GstV4l2Object *v4l2object = NULL; if (GST_IS_V4L2SRC (v4l2elem)) { v4l2object = (GST_V4L2SRC (v4l2elem))->v4l2object; +#ifdef HAVE_EXPERIMENTAL } else if (GST_IS_V4L2SINK (v4l2elem)) { v4l2object = (GST_V4L2SINK (v4l2elem))->v4l2object; +#endif } else { GST_ERROR_OBJECT (v4l2elem, "unknown v4l2 element"); } @@ -454,17 +466,29 @@ gst_v4l2_buffer_pool_destroy (GstV4l2BufferPool * pool) /** * gst_v4l2_buffer_pool_get: - * @pool: the pool + * @pool: the "this" object + * @blocking: should this call suspend until there is a buffer available + * in the buffer pool? * * Get an available buffer in the pool */ GstV4l2Buffer * -gst_v4l2_buffer_pool_get (GstV4l2BufferPool * pool) +gst_v4l2_buffer_pool_get (GstV4l2BufferPool * pool, gboolean blocking) { - GstV4l2Buffer *buf = g_async_queue_try_pop (pool->avail_buffers); + GstV4l2Buffer *buf; - if (buf) + if (blocking) { + buf = g_async_queue_pop (pool->avail_buffers); + } else { + buf = g_async_queue_try_pop (pool->avail_buffers); + } + + if (buf) { + GST_V4L2_BUFFER_POOL_LOCK (pool); GST_BUFFER_SIZE (buf) = buf->vbuffer.length; + GST_BUFFER_FLAG_UNSET (buf, 0xffffffff); + GST_V4L2_BUFFER_POOL_UNLOCK (pool); + } pool->running = TRUE; @@ -545,8 +569,6 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool) GST_DEBUG_OBJECT (pool->v4l2elem, "num_live_buffers++: %d", pool->num_live_buffers); - GST_V4L2_BUFFER_POOL_UNLOCK (pool); - /* set top/bottom field first if v4l2_buffer has the information */ if (buffer.field == V4L2_FIELD_INTERLACED_TB) GST_BUFFER_FLAG_SET (pool_buffer, GST_VIDEO_BUFFER_TFF); @@ -556,6 +578,8 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool) /* this can change at every frame, esp. with jpeg */ GST_BUFFER_SIZE (pool_buffer) = buffer.bytesused; + GST_V4L2_BUFFER_POOL_UNLOCK (pool); + return pool_buffer; } diff --git a/sys/v4l2/gstv4l2bufferpool.h b/sys/v4l2/gstv4l2bufferpool.h index 70ab082f7b..caad9ac2fc 100644 --- a/sys/v4l2/gstv4l2bufferpool.h +++ b/sys/v4l2/gstv4l2bufferpool.h @@ -82,7 +82,7 @@ void gst_v4l2_buffer_pool_destroy (GstV4l2BufferPool * pool); GstV4l2BufferPool *gst_v4l2_buffer_pool_new (GstElement *v4l2elem, gint fd, gint num_buffers, GstCaps * caps, gboolean requeuebuf, enum v4l2_buf_type type); -GstV4l2Buffer *gst_v4l2_buffer_pool_get (GstV4l2BufferPool *pool); +GstV4l2Buffer *gst_v4l2_buffer_pool_get (GstV4l2BufferPool *pool, gboolean blocking); gboolean gst_v4l2_buffer_pool_qbuf (GstV4l2BufferPool *pool, GstV4l2Buffer *buf); GstV4l2Buffer *gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool *pool); diff --git a/sys/v4l2/gstv4l2object.c b/sys/v4l2/gstv4l2object.c index b93a4d07ac..49d70428be 100644 --- a/sys/v4l2/gstv4l2object.c +++ b/sys/v4l2/gstv4l2object.c @@ -34,13 +34,20 @@ #include "v4l2_calls.h" #include "gstv4l2tuner.h" -#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ +#ifdef HAVE_XVIDEO #include "gstv4l2xoverlay.h" #endif #include "gstv4l2colorbalance.h" #include "gst/gst-i18n-plugin.h" +/* videodev2.h is not versioned and we can't easily check for the presence + * of enum values at compile time, but the V4L2_CAP_VIDEO_OUTPUT_OVERLAY define + * was added in the same commit as V4L2_FIELD_INTERLACED_{TB,BT} (b2787845) */ +#ifndef V4L2_CAP_VIDEO_OUTPUT_OVERLAY +#define V4L2_FIELD_INTERLACED_TB 8 +#define V4L2_FIELD_INTERLACED_BT 9 +#endif GST_DEBUG_CATEGORY_EXTERN (v4l2_debug); #define GST_CAT_DEFAULT v4l2_debug @@ -695,7 +702,7 @@ gst_v4l2_object_start (GstV4l2Object * v4l2object) else return FALSE; -#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ +#ifdef HAVE_XVIDEO gst_v4l2_xoverlay_start (v4l2object); #endif @@ -705,7 +712,7 @@ gst_v4l2_object_start (GstV4l2Object * v4l2object) gboolean gst_v4l2_object_stop (GstV4l2Object * v4l2object) { -#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ +#ifdef HAVE_XVIDEO gst_v4l2_xoverlay_stop (v4l2object); #endif @@ -767,6 +774,7 @@ static const GstV4L2FormatDesc gst_v4l2_formats[] = { /* compressed formats */ {V4L2_PIX_FMT_MJPEG, TRUE}, {V4L2_PIX_FMT_JPEG, TRUE}, + {V4L2_PIX_FMT_PJPG, TRUE}, {V4L2_PIX_FMT_DV, TRUE}, {V4L2_PIX_FMT_MPEG, FALSE}, @@ -806,10 +814,13 @@ gst_v4l2_object_get_format_from_fourcc (GstV4l2Object * v4l2object, if (fmt->pixelformat == fourcc) return fmt; /* special case for jpeg */ - if ((fmt->pixelformat == V4L2_PIX_FMT_MJPEG && fourcc == V4L2_PIX_FMT_JPEG) - || (fmt->pixelformat == V4L2_PIX_FMT_JPEG - && fourcc == V4L2_PIX_FMT_MJPEG)) { - return fmt; + if (fmt->pixelformat == V4L2_PIX_FMT_MJPEG || + fmt->pixelformat == V4L2_PIX_FMT_JPEG || + fmt->pixelformat == V4L2_PIX_FMT_PJPG) { + if (fourcc == V4L2_PIX_FMT_JPEG + || fourcc == V4L2_PIX_FMT_MJPEG || fourcc == V4L2_PIX_FMT_PJPG) { + return fmt; + } } walk = g_slist_next (walk); } @@ -847,6 +858,7 @@ gst_v4l2_object_format_get_rank (const struct v4l2_fmtdesc *fmt) switch (fourcc) { case V4L2_PIX_FMT_MJPEG: + case V4L2_PIX_FMT_PJPG: rank = JPEG_BASE_RANK; break; case V4L2_PIX_FMT_JPEG: @@ -1065,6 +1077,7 @@ gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc) switch (fourcc) { case V4L2_PIX_FMT_MJPEG: /* Motion-JPEG */ + case V4L2_PIX_FMT_PJPG: /* Progressive-JPEG */ case V4L2_PIX_FMT_JPEG: /* JFIF JPEG */ structure = gst_structure_new ("image/jpeg", NULL); break; @@ -1302,8 +1315,8 @@ gst_v4l2_object_get_all_caps (void) */ gboolean gst_v4l2_object_get_caps_info (GstV4l2Object * v4l2object, GstCaps * caps, - struct v4l2_fmtdesc ** format, gint * w, gint * h, guint * fps_n, - guint * fps_d, guint * size) + struct v4l2_fmtdesc ** format, gint * w, gint * h, + gboolean * interlaced, guint * fps_n, guint * fps_d, guint * size) { GstStructure *structure; const GValue *framerate; @@ -1332,6 +1345,9 @@ gst_v4l2_object_get_caps_info (GstV4l2Object * v4l2object, GstCaps * caps, if (!gst_structure_get_int (structure, "height", h)) return FALSE; + if (!gst_structure_get_boolean (structure, "interlaced", interlaced)) + *interlaced = FALSE; + framerate = gst_structure_get_value (structure, "framerate"); if (!framerate) return FALSE; @@ -1926,15 +1942,15 @@ gst_v4l2_object_get_nearest_size (GstV4l2Object * v4l2object, fmt.fmt.pix.width = *width; fmt.fmt.pix.height = *height; fmt.fmt.pix.pixelformat = pixelformat; - fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + fmt.fmt.pix.field = V4L2_FIELD_NONE; r = v4l2_ioctl (fd, VIDIOC_TRY_FMT, &fmt); if (r < 0 && errno == EINVAL) { - /* try again with progressive video */ + /* try again with interlaced video */ fmt.fmt.pix.width = *width; fmt.fmt.pix.height = *height; fmt.fmt.pix.pixelformat = pixelformat; - fmt.fmt.pix.field = V4L2_FIELD_NONE; + fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; r = v4l2_ioctl (fd, VIDIOC_TRY_FMT, &fmt); } @@ -1976,6 +1992,7 @@ gst_v4l2_object_get_nearest_size (GstV4l2Object * v4l2object, *height = fmt.fmt.pix.height; switch (fmt.fmt.pix.field) { + case V4L2_FIELD_ANY: case V4L2_FIELD_NONE: *interlaced = FALSE; break; @@ -1987,7 +2004,7 @@ gst_v4l2_object_get_nearest_size (GstV4l2Object * v4l2object, default: GST_WARNING_OBJECT (v4l2object->element, "Unsupported field type for %" GST_FOURCC_FORMAT "@%ux%u", - GST_FOURCC_ARGS (pixelformat), width, height); + GST_FOURCC_ARGS (pixelformat), *width, *height); return FALSE; } @@ -1997,10 +2014,22 @@ gst_v4l2_object_get_nearest_size (GstV4l2Object * v4l2object, gboolean gst_v4l2_object_set_format (GstV4l2Object * v4l2object, guint32 pixelformat, - guint32 width, guint32 height) + guint32 width, guint32 height, gboolean interlaced) { gint fd = v4l2object->video_fd; struct v4l2_format format; + enum v4l2_field field; + + if (interlaced) { + GST_DEBUG_OBJECT (v4l2object->element, "interlaced video"); + /* ideally we would differentiate between types of interlaced video + * but there is not sufficient information in the caps.. + */ + field = V4L2_FIELD_INTERLACED; + } else { + GST_DEBUG_OBJECT (v4l2object->element, "progressive video"); + field = V4L2_FIELD_NONE; + } GST_DEBUG_OBJECT (v4l2object->element, "Setting format to %dx%d, format " "%" GST_FOURCC_FORMAT, width, height, GST_FOURCC_ARGS (pixelformat)); @@ -2020,7 +2049,8 @@ gst_v4l2_object_set_format (GstV4l2Object * v4l2object, guint32 pixelformat, if (format.type == v4l2object->type && format.fmt.pix.width == width && format.fmt.pix.height == height && - format.fmt.pix.pixelformat == pixelformat) { + format.fmt.pix.pixelformat == pixelformat && + format.fmt.pix.field == field) { /* Nothing to do. We want to succeed immediately * here because setting the same format back * can still fail due to EBUSY. By short-circuiting @@ -2037,25 +2067,10 @@ gst_v4l2_object_set_format (GstV4l2Object * v4l2object, guint32 pixelformat, format.fmt.pix.width = width; format.fmt.pix.height = height; format.fmt.pix.pixelformat = pixelformat; - /* FIXME: request whole frames; need to use gstreamer interlace support - * (INTERLACED mode returns frames where the fields have already been - * combined, there are other modes for requesting fields individually) */ - format.fmt.pix.field = V4L2_FIELD_INTERLACED; + format.fmt.pix.field = field; if (v4l2_ioctl (fd, VIDIOC_S_FMT, &format) < 0) { - /* we might also get EBUSY here */ - if (errno != EINVAL) - goto set_fmt_failed; - - GST_DEBUG_OBJECT (v4l2object->element, "trying again..."); - - /* try again with progressive video */ - format.fmt.pix.width = width; - format.fmt.pix.height = height; - format.fmt.pix.pixelformat = pixelformat; - format.fmt.pix.field = V4L2_FIELD_NONE; - if (v4l2_ioctl (fd, VIDIOC_S_FMT, &format) < 0) - goto set_fmt_failed; + goto set_fmt_failed; } if (format.fmt.pix.width != width || format.fmt.pix.height != height) diff --git a/sys/v4l2/gstv4l2object.h b/sys/v4l2/gstv4l2object.h index 1b38672103..a0dd41ce11 100644 --- a/sys/v4l2/gstv4l2object.h +++ b/sys/v4l2/gstv4l2object.h @@ -180,7 +180,7 @@ GstCaps* gst_v4l2_object_probe_caps_for_format (GstV4l2Object *v4l2object, gboolean gst_v4l2_object_get_caps_info (GstV4l2Object *v4l2object, GstCaps *caps, struct v4l2_fmtdesc **format, gint *w, gint *h, - guint *fps_n, guint *fps_d, guint *size); + gboolean * interlaced, guint *fps_n, guint *fps_d, guint *size); GSList* gst_v4l2_object_get_format_list (GstV4l2Object *v4l2object); @@ -189,7 +189,7 @@ GstCaps* gst_v4l2_object_get_all_caps (void); GstStructure* gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc); -gboolean gst_v4l2_object_set_format (GstV4l2Object *v4l2object, guint32 pixelformat, guint32 width, guint32 height); +gboolean gst_v4l2_object_set_format (GstV4l2Object *v4l2object, guint32 pixelformat, guint32 width, guint32 height, gboolean interlaced); gboolean gst_v4l2_object_start_streaming (GstV4l2Object *v4l2object); gboolean gst_v4l2_object_stop_streaming (GstV4l2Object *v4l2object); diff --git a/sys/v4l2/gstv4l2sink.c b/sys/v4l2/gstv4l2sink.c index 1fde82c292..aa6785cfea 100644 --- a/sys/v4l2/gstv4l2sink.c +++ b/sys/v4l2/gstv4l2sink.c @@ -33,6 +33,18 @@ * |[ * gst-launch videotestsrc ! v4l2sink device=/dev/video1 * ]| This pipeline displays a test pattern on /dev/video1 + * |[ + * gst-launch -v videotestsrc ! navigationtest ! v4l2sink + * ]| A pipeline to test navigation events. + * While moving the mouse pointer over the test signal you will see a black box + * following the mouse pointer. If you press the mouse button somewhere on the + * video and release it somewhere else a green box will appear where you pressed + * the button and a red one where you released it. (The navigationtest element + * is part of gst-plugins-good.) You can observe here that even if the images + * are scaled through hardware the pointer coordinates are converted back to the + * original video frame geometry so that the box can be drawn to the correct + * position. This also handles borders correctly, limiting coordinates to the + * image area * */ @@ -43,7 +55,7 @@ #include "gstv4l2colorbalance.h" -#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ +#ifdef HAVE_XVIDEO #include "gstv4l2xoverlay.h" #endif #include "gstv4l2vidorient.h" @@ -56,7 +68,8 @@ GST_DEBUG_CATEGORY (v4l2sink_debug); #define GST_CAT_DEFAULT v4l2sink_debug -#define PROP_DEF_QUEUE_SIZE 8 +#define PROP_DEF_QUEUE_SIZE 12 +#define PROP_DEF_MIN_QUEUED_BUFS 1 #define DEFAULT_PROP_DEVICE "/dev/video1" enum @@ -64,16 +77,21 @@ enum PROP_0, V4L2_STD_OBJECT_PROPS, PROP_QUEUE_SIZE, + PROP_MIN_QUEUED_BUFS, PROP_OVERLAY_TOP, PROP_OVERLAY_LEFT, PROP_OVERLAY_WIDTH, PROP_OVERLAY_HEIGHT, + PROP_CROP_TOP, + PROP_CROP_LEFT, + PROP_CROP_WIDTH, + PROP_CROP_HEIGHT, }; GST_IMPLEMENT_V4L2_PROBE_METHODS (GstV4l2SinkClass, gst_v4l2sink); GST_IMPLEMENT_V4L2_COLOR_BALANCE_METHODS (GstV4l2Sink, gst_v4l2sink); -#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ +#ifdef HAVE_XVIDEO GST_IMPLEMENT_V4L2_XOVERLAY_METHODS (GstV4l2Sink, gst_v4l2sink); #endif GST_IMPLEMENT_V4L2_VIDORIENT_METHODS (GstV4l2Sink, gst_v4l2sink); @@ -83,8 +101,9 @@ gst_v4l2sink_iface_supported (GstImplementsInterface * iface, GType iface_type) { GstV4l2Object *v4l2object = GST_V4L2SINK (iface)->v4l2object; -#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ +#ifdef HAVE_XVIDEO g_assert (iface_type == GST_TYPE_X_OVERLAY || + iface_type == GST_TYPE_NAVIGATION || iface_type == GST_TYPE_COLOR_BALANCE || iface_type == GST_TYPE_VIDEO_ORIENTATION); #else @@ -95,9 +114,11 @@ gst_v4l2sink_iface_supported (GstImplementsInterface * iface, GType iface_type) if (v4l2object->video_fd == -1) return FALSE; -#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ - if (iface_type == GST_TYPE_X_OVERLAY && !GST_V4L2_IS_OVERLAY (v4l2object)) - return FALSE; +#ifdef HAVE_XVIDEO + if (!GST_V4L2_IS_OVERLAY (v4l2object)) { + if (iface_type == GST_TYPE_X_OVERLAY || iface_type == GST_TYPE_NAVIGATION) + return FALSE; + } #endif return TRUE; @@ -112,6 +133,16 @@ gst_v4l2sink_interface_init (GstImplementsInterfaceClass * klass) klass->supported = gst_v4l2sink_iface_supported; } +#ifdef HAVE_XVIDEO +static void gst_v4l2sink_navigation_send_event (GstNavigation * navigation, + GstStructure * structure); +static void +gst_v4l2sink_navigation_init (GstNavigationInterface * iface) +{ + iface->send_event = gst_v4l2sink_navigation_send_event; +} +#endif + static void gst_v4l2sink_init_interfaces (GType type) { @@ -120,12 +151,17 @@ gst_v4l2sink_init_interfaces (GType type) NULL, NULL, }; -#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ +#ifdef HAVE_XVIDEO static const GInterfaceInfo v4l2_xoverlay_info = { (GInterfaceInitFunc) gst_v4l2sink_xoverlay_interface_init, NULL, NULL, }; + static const GInterfaceInfo v4l2_navigation_info = { + (GInterfaceInitFunc) gst_v4l2sink_navigation_init, + NULL, + NULL, + }; #endif static const GInterfaceInfo v4l2_colorbalance_info = { (GInterfaceInitFunc) gst_v4l2sink_color_balance_interface_init, @@ -145,8 +181,10 @@ gst_v4l2sink_init_interfaces (GType type) g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, &v4l2iface_info); -#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ +#ifdef HAVE_XVIDEO g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &v4l2_xoverlay_info); + g_type_add_interface_static (type, + GST_TYPE_NAVIGATION, &v4l2_navigation_info); #endif g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &v4l2_colorbalance_info); @@ -183,7 +221,6 @@ static GstFlowReturn gst_v4l2sink_buffer_alloc (GstBaseSink * bsink, static GstFlowReturn gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf); - static void gst_v4l2sink_base_init (gpointer g_class) { @@ -229,6 +266,12 @@ gst_v4l2sink_class_init (GstV4l2SinkClass * klass) "Number of buffers to be enqueud in the driver in streaming mode", GST_V4L2_MIN_BUFFERS, GST_V4L2_MAX_BUFFERS, PROP_DEF_QUEUE_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_MIN_QUEUED_BUFS, + g_param_spec_uint ("min-queued-bufs", "Minimum queued bufs", + "Minimum number of queued bufs; v4l2sink won't dqbuf if the driver " + "doesn't have more than this number (which normally you shouldn't change)", + 0, GST_V4L2_MAX_BUFFERS, PROP_DEF_MIN_QUEUED_BUFS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_OVERLAY_TOP, g_param_spec_int ("overlay-top", "Overlay top", "The topmost (y) coordinate of the video overlay; top left corner of screen is 0,0", @@ -246,10 +289,26 @@ gst_v4l2sink_class_init (GstV4l2SinkClass * klass) "The height of the video overlay; default is equal to negotiated image height", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_CROP_TOP, + g_param_spec_int ("crop-top", "Crop top", + "The topmost (y) coordinate of the video crop; top left corner of image is 0,0", + 0x80000000, 0x7fffffff, 0, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_CROP_LEFT, + g_param_spec_int ("crop-left", "Crop left", + "The leftmost (x) coordinate of the video crop; top left corner of image is 0,0", + 0x80000000, 0x7fffffff, 0, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_CROP_WIDTH, + g_param_spec_uint ("crop-width", "Crop width", + "The width of the video crop; default is equal to negotiated image width", + 0, 0xffffffff, 0, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_CROP_HEIGHT, + g_param_spec_uint ("crop-height", "Crop height", + "The height of the video crop; default is equal to negotiated image height", + 0, 0xffffffff, 0, G_PARAM_READWRITE)); + basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_v4l2sink_get_caps); basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_v4l2sink_set_caps); basesink_class->buffer_alloc = GST_DEBUG_FUNCPTR (gst_v4l2sink_buffer_alloc); - basesink_class->preroll = GST_DEBUG_FUNCPTR (gst_v4l2sink_show_frame); basesink_class->render = GST_DEBUG_FUNCPTR (gst_v4l2sink_show_frame); } @@ -258,7 +317,7 @@ gst_v4l2sink_init (GstV4l2Sink * v4l2sink, GstV4l2SinkClass * klass) { v4l2sink->v4l2object = gst_v4l2_object_new (GST_ELEMENT (v4l2sink), V4L2_BUF_TYPE_VIDEO_OUTPUT, DEFAULT_PROP_DEVICE, - gst_v4l2_get_input, gst_v4l2_set_input, NULL); + gst_v4l2_get_output, gst_v4l2_set_output, NULL); /* same default value for video output device as is used for * v4l2src/capture is no good.. so lets set a saner default @@ -269,11 +328,13 @@ gst_v4l2sink_init (GstV4l2Sink * v4l2sink, GstV4l2SinkClass * klass) /* number of buffers requested */ v4l2sink->num_buffers = PROP_DEF_QUEUE_SIZE; + v4l2sink->min_queued_bufs = PROP_DEF_MIN_QUEUED_BUFS; v4l2sink->probed_caps = NULL; v4l2sink->current_caps = NULL; v4l2sink->overlay_fields_set = 0; + v4l2sink->crop_fields_set = 0; v4l2sink->state = 0; } @@ -315,15 +376,15 @@ enum }; /* - * flags to indicate which overlay properties the user has set (and therefore - * which ones should override the defaults from the driver) + * flags to indicate which overlay/crop properties the user has set (and + * therefore which ones should override the defaults from the driver) */ enum { - OVERLAY_TOP_SET = 0x01, - OVERLAY_LEFT_SET = 0x02, - OVERLAY_WIDTH_SET = 0x04, - OVERLAY_HEIGHT_SET = 0x08 + RECT_TOP_SET = 0x01, + RECT_LEFT_SET = 0x02, + RECT_WIDTH_SET = 0x04, + RECT_HEIGHT_SET = 0x08 }; static void @@ -340,24 +401,80 @@ gst_v4l2sink_sync_overlay_fields (GstV4l2Sink * v4l2sink) memset (&format, 0x00, sizeof (struct v4l2_format)); format.type = V4L2_BUF_TYPE_VIDEO_OVERLAY; - g_return_if_fail (v4l2_ioctl (fd, VIDIOC_G_FMT, &format) >= 0); + if (v4l2_ioctl (fd, VIDIOC_G_FMT, &format) < 0) { + GST_WARNING_OBJECT (v4l2sink, "VIDIOC_G_FMT failed"); + return; + } - if (v4l2sink->overlay_fields_set & OVERLAY_TOP_SET) + GST_DEBUG_OBJECT (v4l2sink, + "setting overlay: overlay_fields_set=0x%02x, top=%d, left=%d, width=%d, height=%d", + v4l2sink->overlay_fields_set, + v4l2sink->overlay.top, v4l2sink->overlay.left, + v4l2sink->overlay.width, v4l2sink->overlay.height); + + if (v4l2sink->overlay_fields_set & RECT_TOP_SET) format.fmt.win.w.top = v4l2sink->overlay.top; - if (v4l2sink->overlay_fields_set & OVERLAY_LEFT_SET) + if (v4l2sink->overlay_fields_set & RECT_LEFT_SET) format.fmt.win.w.left = v4l2sink->overlay.left; - if (v4l2sink->overlay_fields_set & OVERLAY_WIDTH_SET) + if (v4l2sink->overlay_fields_set & RECT_WIDTH_SET) format.fmt.win.w.width = v4l2sink->overlay.width; - if (v4l2sink->overlay_fields_set & OVERLAY_HEIGHT_SET) + if (v4l2sink->overlay_fields_set & RECT_HEIGHT_SET) format.fmt.win.w.height = v4l2sink->overlay.height; - g_return_if_fail (v4l2_ioctl (fd, VIDIOC_S_FMT, &format) >= 0); - v4l2sink->overlay_fields_set = 0; + if (v4l2_ioctl (fd, VIDIOC_S_FMT, &format) < 0) { + GST_WARNING_OBJECT (v4l2sink, "VIDIOC_S_FMT failed"); + return; + } + v4l2sink->overlay_fields_set = 0; v4l2sink->overlay = format.fmt.win.w; } } +static void +gst_v4l2sink_sync_crop_fields (GstV4l2Sink * v4l2sink) +{ + if (!v4l2sink->crop_fields_set) + return; + + if (GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) { + + gint fd = v4l2sink->v4l2object->video_fd; + struct v4l2_crop crop; + + memset (&crop, 0x00, sizeof (struct v4l2_crop)); + crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + if (v4l2_ioctl (fd, VIDIOC_G_CROP, &crop) < 0) { + GST_WARNING_OBJECT (v4l2sink, "VIDIOC_G_CROP failed"); + return; + } + + GST_DEBUG_OBJECT (v4l2sink, + "setting crop: crop_fields_set=0x%02x, top=%d, left=%d, width=%d, height=%d", + v4l2sink->crop_fields_set, + v4l2sink->crop.top, v4l2sink->crop.left, + v4l2sink->crop.width, v4l2sink->crop.height); + + if (v4l2sink->crop_fields_set & RECT_TOP_SET) + crop.c.top = v4l2sink->crop.top; + if (v4l2sink->crop_fields_set & RECT_LEFT_SET) + crop.c.left = v4l2sink->crop.left; + if (v4l2sink->crop_fields_set & RECT_WIDTH_SET) + crop.c.width = v4l2sink->crop.width; + if (v4l2sink->crop_fields_set & RECT_HEIGHT_SET) + crop.c.height = v4l2sink->crop.height; + + if (v4l2_ioctl (fd, VIDIOC_S_CROP, &crop) < 0) { + GST_WARNING_OBJECT (v4l2sink, "VIDIOC_S_CROP failed"); + return; + } + + v4l2sink->crop_fields_set = 0; + v4l2sink->crop = crop.c; + } +} + static void gst_v4l2sink_set_property (GObject * object, @@ -371,26 +488,49 @@ gst_v4l2sink_set_property (GObject * object, case PROP_QUEUE_SIZE: v4l2sink->num_buffers = g_value_get_uint (value); break; + case PROP_MIN_QUEUED_BUFS: + v4l2sink->min_queued_bufs = g_value_get_uint (value); + break; case PROP_OVERLAY_TOP: v4l2sink->overlay.top = g_value_get_int (value); - v4l2sink->overlay_fields_set |= OVERLAY_TOP_SET; + v4l2sink->overlay_fields_set |= RECT_TOP_SET; gst_v4l2sink_sync_overlay_fields (v4l2sink); break; case PROP_OVERLAY_LEFT: v4l2sink->overlay.left = g_value_get_int (value); - v4l2sink->overlay_fields_set |= OVERLAY_LEFT_SET; + v4l2sink->overlay_fields_set |= RECT_LEFT_SET; gst_v4l2sink_sync_overlay_fields (v4l2sink); break; case PROP_OVERLAY_WIDTH: v4l2sink->overlay.width = g_value_get_uint (value); - v4l2sink->overlay_fields_set |= OVERLAY_WIDTH_SET; + v4l2sink->overlay_fields_set |= RECT_WIDTH_SET; gst_v4l2sink_sync_overlay_fields (v4l2sink); break; case PROP_OVERLAY_HEIGHT: v4l2sink->overlay.height = g_value_get_uint (value); - v4l2sink->overlay_fields_set |= OVERLAY_HEIGHT_SET; + v4l2sink->overlay_fields_set |= RECT_HEIGHT_SET; gst_v4l2sink_sync_overlay_fields (v4l2sink); break; + case PROP_CROP_TOP: + v4l2sink->crop.top = g_value_get_int (value); + v4l2sink->crop_fields_set |= RECT_TOP_SET; + gst_v4l2sink_sync_crop_fields (v4l2sink); + break; + case PROP_CROP_LEFT: + v4l2sink->crop.left = g_value_get_int (value); + v4l2sink->crop_fields_set |= RECT_LEFT_SET; + gst_v4l2sink_sync_crop_fields (v4l2sink); + break; + case PROP_CROP_WIDTH: + v4l2sink->crop.width = g_value_get_uint (value); + v4l2sink->crop_fields_set |= RECT_WIDTH_SET; + gst_v4l2sink_sync_crop_fields (v4l2sink); + break; + case PROP_CROP_HEIGHT: + v4l2sink->crop.height = g_value_get_uint (value); + v4l2sink->crop_fields_set |= RECT_HEIGHT_SET; + gst_v4l2sink_sync_crop_fields (v4l2sink); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -411,6 +551,9 @@ gst_v4l2sink_get_property (GObject * object, case PROP_QUEUE_SIZE: g_value_set_uint (value, v4l2sink->num_buffers); break; + case PROP_MIN_QUEUED_BUFS: + g_value_set_uint (value, v4l2sink->min_queued_bufs); + break; case PROP_OVERLAY_TOP: g_value_set_int (value, v4l2sink->overlay.top); break; @@ -423,6 +566,18 @@ gst_v4l2sink_get_property (GObject * object, case PROP_OVERLAY_HEIGHT: g_value_set_uint (value, v4l2sink->overlay.height); break; + case PROP_CROP_TOP: + g_value_set_int (value, v4l2sink->crop.top); + break; + case PROP_CROP_LEFT: + g_value_set_int (value, v4l2sink->crop.left); + break; + case PROP_CROP_WIDTH: + g_value_set_uint (value, v4l2sink->crop.width); + break; + case PROP_CROP_HEIGHT: + g_value_set_uint (value, v4l2sink->crop.height); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -458,7 +613,7 @@ gst_v4l2sink_change_state (GstElement * element, GstStateChange transition) if (!gst_v4l2_object_stop_streaming (v4l2sink->v4l2object)) { return GST_STATE_CHANGE_FAILURE; } - v4l2sink->state = STATE_OFF; + v4l2sink->state = STATE_PENDING_STREAMON; } break; case GST_STATE_CHANGE_READY_TO_NULL: @@ -468,6 +623,7 @@ gst_v4l2sink_change_state (GstElement * element, GstStateChange transition) /* close the device */ if (!gst_v4l2_object_stop (v4l2sink->v4l2object)) return GST_STATE_CHANGE_FAILURE; + v4l2sink->state = STATE_OFF; break; default: break; @@ -539,6 +695,7 @@ gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps) { GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink); gint w = 0, h = 0; + gboolean interlaced; struct v4l2_fmtdesc *format; guint fps_n, fps_d; guint size; @@ -574,7 +731,7 @@ gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps) /* we want our own v4l2 type of fourcc codes */ if (!gst_v4l2_object_get_caps_info (v4l2sink->v4l2object, caps, - &format, &w, &h, &fps_n, &fps_d, &size)) { + &format, &w, &h, &interlaced, &fps_n, &fps_d, &size)) { GST_DEBUG_OBJECT (v4l2sink, "can't get capture format from caps %p", caps); return FALSE; } @@ -584,13 +741,20 @@ gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps) return FALSE; } - if (!gst_v4l2_object_set_format (v4l2sink->v4l2object, format->pixelformat, w, - h)) { + if (!gst_v4l2_object_set_format (v4l2sink->v4l2object, format->pixelformat, + w, h, interlaced)) { /* error already posted */ return FALSE; } - gst_v4l2sink_sync_overlay_fields (v4l2sink); + v4l2sink->video_width = w; + v4l2sink->video_height = h; + + /* TODO: videosink width/height should be scaled according to + * pixel-aspect-ratio + */ + GST_VIDEO_SINK_WIDTH (v4l2sink) = w; + GST_VIDEO_SINK_HEIGHT (v4l2sink) = h; v4l2sink->current_caps = gst_caps_ref (caps); @@ -623,6 +787,14 @@ gst_v4l2sink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, V4L2_BUF_TYPE_VIDEO_OUTPUT))) { return GST_FLOW_ERROR; } + + gst_v4l2sink_sync_overlay_fields (v4l2sink); + gst_v4l2sink_sync_crop_fields (v4l2sink); + +#ifdef HAVE_XVIDEO + gst_v4l2_xoverlay_prepare_xwindow_id (v4l2sink->v4l2object, TRUE); +#endif + v4l2sink->state = STATE_PENDING_STREAMON; GST_INFO_OBJECT (v4l2sink, "outputting buffers via mmap()"); @@ -633,7 +805,7 @@ gst_v4l2sink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, } } - v4l2buf = gst_v4l2_buffer_pool_get (v4l2sink->pool); + v4l2buf = gst_v4l2_buffer_pool_get (v4l2sink->pool, TRUE); if (G_LIKELY (v4l2buf)) { GST_DEBUG_OBJECT (v4l2sink, "allocated buffer: %p", v4l2buf); @@ -662,6 +834,29 @@ gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf) if (!GST_IS_V4L2_BUFFER (buf)) { GstFlowReturn ret; + /* special case check for sub-buffers: In certain cases, places like + * GstBaseTransform, which might check that the buffer is writable + * before copying metadata, timestamp, and such, will find that the + * buffer has more than one reference to it. In these cases, they + * will create a sub-buffer with an offset=0 and length equal to the + * original buffer size. + * + * This could happen in two scenarios: (1) a tee in the pipeline, and + * (2) because the refcnt is incremented in gst_mini_object_free() + * before the finalize function is called, and decremented after it + * returns.. but returning this buffer to the buffer pool in the + * finalize function, could wake up a thread blocked in _buffer_alloc() + * which could run and get a buffer w/ refcnt==2 before the thread + * originally unref'ing the buffer returns from finalize function and + * decrements the refcnt back to 1! + */ + if (buf->parent && + (GST_BUFFER_DATA (buf) == GST_BUFFER_DATA (buf->parent)) && + (GST_BUFFER_SIZE (buf) == GST_BUFFER_SIZE (buf->parent))) { + GST_DEBUG_OBJECT (v4l2sink, "I have a sub-buffer!"); + return gst_v4l2sink_show_frame (bsink, buf->parent); + } + GST_DEBUG_OBJECT (v4l2sink, "slow-path.. I got a %s so I need to memcpy", g_type_name (G_OBJECT_TYPE (buf))); @@ -670,7 +865,9 @@ gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf) &newbuf); if (GST_FLOW_OK != ret) { - return ret; + GST_DEBUG_OBJECT (v4l2sink, + "dropping frame! Consider increasing 'queue-size' property!"); + return GST_FLOW_OK; } memcpy (GST_BUFFER_DATA (newbuf), @@ -700,7 +897,8 @@ gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf) * just queued, then dequeue one immediately to make it available via * _buffer_alloc(): */ - if (gst_v4l2_buffer_pool_available_buffers (v4l2sink->pool) > 1) { + if (gst_v4l2_buffer_pool_available_buffers (v4l2sink->pool) > + v4l2sink->min_queued_bufs) { GstV4l2Buffer *v4l2buf = gst_v4l2_buffer_pool_dqbuf (v4l2sink->pool); /* note: if we get a buf, we don't want to use it directly (because @@ -715,3 +913,47 @@ gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf) return GST_FLOW_OK; } + +#ifdef HAVE_XVIDEO +static void +gst_v4l2sink_navigation_send_event (GstNavigation * navigation, + GstStructure * structure) +{ + GstV4l2Sink *v4l2sink = GST_V4L2SINK (navigation); + GstV4l2Xv *xv = v4l2sink->v4l2object->xv; + GstPad *peer; + + if (!xv) + return; + + if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (v4l2sink)))) { + GstVideoRectangle rect; + gdouble x, y, xscale = 1.0, yscale = 1.0; + + gst_v4l2_xoverlay_get_render_rect (v4l2sink->v4l2object, &rect); + + /* We calculate scaling using the original video frames geometry to + * include pixel aspect ratio scaling. + */ + xscale = (gdouble) v4l2sink->video_width / rect.w; + yscale = (gdouble) v4l2sink->video_height / rect.h; + + /* Converting pointer coordinates to the non scaled geometry */ + if (gst_structure_get_double (structure, "pointer_x", &x)) { + x = MIN (x, rect.x + rect.w); + x = MAX (x - rect.x, 0); + gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, + (gdouble) x * xscale, NULL); + } + if (gst_structure_get_double (structure, "pointer_y", &y)) { + y = MIN (y, rect.y + rect.h); + y = MAX (y - rect.y, 0); + gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, + (gdouble) y * yscale, NULL); + } + + gst_pad_send_event (peer, gst_event_new_navigation (structure)); + gst_object_unref (peer); + } +} +#endif diff --git a/sys/v4l2/gstv4l2sink.h b/sys/v4l2/gstv4l2sink.h index 71553cbae7..8fe82221ae 100644 --- a/sys/v4l2/gstv4l2sink.h +++ b/sys/v4l2/gstv4l2sink.h @@ -58,18 +58,21 @@ struct _GstV4l2Sink { GstCaps *current_caps; /* the current negotiated caps */ GstV4l2BufferPool *pool; guint32 num_buffers; + guint32 min_queued_bufs; + + gint video_width, video_height; /* original (unscaled) video w/h */ /* - * field to store requested overlay-top/left/width/height props: + * field to store requested overlay and crop top/left/width/height props: * note, could maybe be combined with 'vwin' field in GstV4l2Object? */ - struct v4l2_rect overlay; + struct v4l2_rect overlay, crop; /* - * bitmask to track which 'overlay' fields user has requested by + * bitmask to track which overlay and crop fields user has requested by * setting properties: */ - guint8 overlay_fields_set; + guint8 overlay_fields_set, crop_fields_set; guint8 state; }; diff --git a/sys/v4l2/gstv4l2src.c b/sys/v4l2/gstv4l2src.c index 4724fe9675..4a37d354b9 100644 --- a/sys/v4l2/gstv4l2src.c +++ b/sys/v4l2/gstv4l2src.c @@ -44,6 +44,8 @@ #include #endif +#undef HAVE_XVIDEO + #include #include #include "v4l2src_calls.h" @@ -51,7 +53,7 @@ #include "gstv4l2colorbalance.h" #include "gstv4l2tuner.h" -#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ +#ifdef HAVE_XVIDEO #include "gstv4l2xoverlay.h" #endif #include "gstv4l2vidorient.h" @@ -79,7 +81,7 @@ enum GST_IMPLEMENT_V4L2_PROBE_METHODS (GstV4l2SrcClass, gst_v4l2src); GST_IMPLEMENT_V4L2_COLOR_BALANCE_METHODS (GstV4l2Src, gst_v4l2src); GST_IMPLEMENT_V4L2_TUNER_METHODS (GstV4l2Src, gst_v4l2src); -#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ +#ifdef HAVE_XVIDEO GST_IMPLEMENT_V4L2_XOVERLAY_METHODS (GstV4l2Src, gst_v4l2src); #endif GST_IMPLEMENT_V4L2_VIDORIENT_METHODS (GstV4l2Src, gst_v4l2src); @@ -92,7 +94,7 @@ gst_v4l2src_iface_supported (GstImplementsInterface * iface, GType iface_type) { GstV4l2Object *v4l2object = GST_V4L2SRC (iface)->v4l2object; -#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ +#ifdef HAVE_XVIDEO g_assert (iface_type == GST_TYPE_TUNER || iface_type == GST_TYPE_X_OVERLAY || iface_type == GST_TYPE_COLOR_BALANCE || @@ -106,7 +108,7 @@ gst_v4l2src_iface_supported (GstImplementsInterface * iface, GType iface_type) if (v4l2object->video_fd == -1) return FALSE; -#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ +#ifdef HAVE_XVIDEO if (iface_type == GST_TYPE_X_OVERLAY && !GST_V4L2_IS_OVERLAY (v4l2object)) return FALSE; #endif @@ -142,7 +144,8 @@ gst_v4l2src_init_interfaces (GType type) NULL, NULL, }; -#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ +#ifdef HAVE_XVIDEO + /* FIXME: does GstXOverlay for v4l2src make sense in a GStreamer context? */ static const GInterfaceInfo v4l2_xoverlay_info = { (GInterfaceInitFunc) gst_v4l2src_xoverlay_interface_init, NULL, @@ -169,7 +172,7 @@ gst_v4l2src_init_interfaces (GType type) g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, &v4l2iface_info); g_type_add_interface_static (type, GST_TYPE_TUNER, &v4l2_tuner_info); -#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */ +#ifdef HAVE_XVIDEO g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &v4l2_xoverlay_info); #endif g_type_add_interface_static (type, @@ -611,6 +614,7 @@ gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps) { GstV4l2Src *v4l2src; gint w = 0, h = 0; + gboolean interlaced; struct v4l2_fmtdesc *format; guint fps_n, fps_d; guint size; @@ -632,7 +636,7 @@ gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps) /* we want our own v4l2 type of fourcc codes */ if (!gst_v4l2_object_get_caps_info (v4l2src->v4l2object, caps, &format, &w, - &h, &fps_n, &fps_d, &size)) { + &h, &interlaced, &fps_n, &fps_d, &size)) { GST_INFO_OBJECT (v4l2src, "can't get capture format from caps %" GST_PTR_FORMAT, caps); return FALSE; @@ -641,8 +645,8 @@ gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps) GST_DEBUG_OBJECT (v4l2src, "trying to set_capture %dx%d at %d/%d fps, " "format %s", w, h, fps_n, fps_d, format->description); - if (!gst_v4l2src_set_capture (v4l2src, format->pixelformat, w, h, fps_n, - fps_d)) + if (!gst_v4l2src_set_capture (v4l2src, format->pixelformat, w, h, + interlaced, fps_n, fps_d)) /* error already posted */ return FALSE; diff --git a/sys/v4l2/gstv4l2xoverlay.c b/sys/v4l2/gstv4l2xoverlay.c index 4c13e5bfdd..c341e19a18 100644 --- a/sys/v4l2/gstv4l2xoverlay.c +++ b/sys/v4l2/gstv4l2xoverlay.c @@ -33,15 +33,19 @@ #include #include +#include + #include "gstv4l2xoverlay.h" #include "gstv4l2object.h" #include "v4l2_calls.h" +#include "gst/gst-i18n-plugin.h" + struct _GstV4l2Xv { Display *dpy; - gint port, idle_id; - GMutex *mutex; + gint port, idle_id, event_id; + GMutex *mutex; /* to serialize calls to X11 */ }; GST_DEBUG_CATEGORY_STATIC (v4l2xv_debug); @@ -101,10 +105,15 @@ gst_v4l2_xoverlay_open (GstV4l2Object * v4l2object) } min = s.st_rdev & 0xff; for (i = 0; i < anum; i++) { - if (!strcmp (ai[i].name, "video4linux2")) { + GST_DEBUG_OBJECT (v4l2object->element, "found adapter: %s", ai[i].name); + if (!strcmp (ai[i].name, "video4linux2") || + !strcmp (ai[i].name, "video4linux")) { if (first_id == 0) first_id = ai[i].base_id; + GST_DEBUG_OBJECT (v4l2object->element, + "first_id=%d, base_id=%lu, min=%d", first_id, ai[i].base_id, min); + /* hmm... */ if (first_id != 0 && ai[i].base_id == first_id + min) id = ai[i].base_id; @@ -124,6 +133,7 @@ gst_v4l2_xoverlay_open (GstV4l2Object * v4l2object) v4l2xv->port = id; v4l2xv->mutex = g_mutex_new (); v4l2xv->idle_id = 0; + v4l2xv->event_id = 0; v4l2object->xv = v4l2xv; if (v4l2object->xwindow_id) { @@ -147,6 +157,8 @@ gst_v4l2_xoverlay_close (GstV4l2Object * v4l2object) g_mutex_free (v4l2xv->mutex); if (v4l2xv->idle_id) g_source_remove (v4l2xv->idle_id); + if (v4l2xv->event_id) + g_source_remove (v4l2xv->event_id); g_free (v4l2xv); v4l2object->xv = NULL; } @@ -165,20 +177,67 @@ gst_v4l2_xoverlay_stop (GstV4l2Object * v4l2object) gst_v4l2_xoverlay_close (v4l2object); } +/* should be called with mutex held */ +static gboolean +get_render_rect (GstV4l2Object * v4l2object, GstVideoRectangle * rect) +{ + GstV4l2Xv *v4l2xv = v4l2object->xv; + if (v4l2xv && v4l2xv->dpy && v4l2object->xwindow_id) { + XWindowAttributes attr; + XGetWindowAttributes (v4l2xv->dpy, v4l2object->xwindow_id, &attr); + /* this is where we'd add support to maintain aspect ratio */ + rect->x = 0; + rect->y = 0; + rect->w = attr.width; + rect->h = attr.height; + return TRUE; + } else { + return FALSE; + } +} + +gboolean +gst_v4l2_xoverlay_get_render_rect (GstV4l2Object * v4l2object, + GstVideoRectangle * rect) +{ + GstV4l2Xv *v4l2xv = v4l2object->xv; + gboolean ret = FALSE; + if (v4l2xv) { + g_mutex_lock (v4l2xv->mutex); + ret = get_render_rect (v4l2object, rect); + g_mutex_unlock (v4l2xv->mutex); + } + return ret; +} + +static void +update_geometry (GstV4l2Object * v4l2object) +{ + GstV4l2Xv *v4l2xv = v4l2object->xv; + GstVideoRectangle rect; + if (!get_render_rect (v4l2object, &rect)) + return; + /* note: we don't pass in valid video x/y/w/h.. currently the xserver + * doesn't need to know these, as they come from v4l2 by setting the + * crop.. + */ + XvPutVideo (v4l2xv->dpy, v4l2xv->port, v4l2object->xwindow_id, + DefaultGC (v4l2xv->dpy, DefaultScreen (v4l2xv->dpy)), + 0, 0, rect.w, rect.h, rect.x, rect.y, rect.w, rect.h); +} + static gboolean idle_refresh (gpointer data) { GstV4l2Object *v4l2object = GST_V4L2_OBJECT (data); GstV4l2Xv *v4l2xv = v4l2object->xv; - XWindowAttributes attr; + + GST_LOG_OBJECT (v4l2object->element, "idle refresh"); if (v4l2xv) { g_mutex_lock (v4l2xv->mutex); - XGetWindowAttributes (v4l2xv->dpy, v4l2object->xwindow_id, &attr); - XvPutVideo (v4l2xv->dpy, v4l2xv->port, v4l2object->xwindow_id, - DefaultGC (v4l2xv->dpy, DefaultScreen (v4l2xv->dpy)), - 0, 0, attr.width, attr.height, 0, 0, attr.width, attr.height); + update_geometry (v4l2object); v4l2xv->idle_id = 0; g_mutex_unlock (v4l2xv->mutex); @@ -188,11 +247,125 @@ idle_refresh (gpointer data) return FALSE; } + +static gboolean +event_refresh (gpointer data) +{ + GstV4l2Object *v4l2object = GST_V4L2_OBJECT (data); + GstV4l2Xv *v4l2xv = v4l2object->xv; + + GST_LOG_OBJECT (v4l2object->element, "event refresh"); + + if (v4l2xv) { + XEvent e; + + g_mutex_lock (v4l2xv->mutex); + + /* If the element supports navigation, collect the relavent input + * events and push them upstream as navigation events + */ + if (GST_IS_NAVIGATION (v4l2object->element)) { + guint pointer_x = 0, pointer_y = 0; + gboolean pointer_moved = FALSE; + + /* We get all pointer motion events, only the last position is + * interesting. + */ + while (XCheckWindowEvent (v4l2xv->dpy, v4l2object->xwindow_id, + PointerMotionMask, &e)) { + switch (e.type) { + case MotionNotify: + pointer_x = e.xmotion.x; + pointer_y = e.xmotion.y; + pointer_moved = TRUE; + break; + default: + break; + } + } + if (pointer_moved) { + GST_DEBUG_OBJECT (v4l2object->element, + "pointer moved over window at %d,%d", pointer_x, pointer_y); + g_mutex_unlock (v4l2xv->mutex); + gst_navigation_send_mouse_event (GST_NAVIGATION (v4l2object->element), + "mouse-move", 0, e.xbutton.x, e.xbutton.y); + g_mutex_lock (v4l2xv->mutex); + } + + /* We get all events on our window to throw them upstream + */ + while (XCheckWindowEvent (v4l2xv->dpy, v4l2object->xwindow_id, + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask, &e)) { + KeySym keysym; + const char *key_str = NULL; + + g_mutex_unlock (v4l2xv->mutex); + + switch (e.type) { + case ButtonPress: + GST_DEBUG_OBJECT (v4l2object->element, + "button %d pressed over window at %d,%d", + e.xbutton.button, e.xbutton.x, e.xbutton.y); + gst_navigation_send_mouse_event (GST_NAVIGATION + (v4l2object->element), "mouse-button-press", e.xbutton.button, + e.xbutton.x, e.xbutton.y); + break; + case ButtonRelease: + GST_DEBUG_OBJECT (v4l2object->element, + "button %d released over window at %d,%d", + e.xbutton.button, e.xbutton.x, e.xbutton.y); + gst_navigation_send_mouse_event (GST_NAVIGATION + (v4l2object->element), "mouse-button-release", e.xbutton.button, + e.xbutton.x, e.xbutton.y); + break; + case KeyPress: + case KeyRelease: + g_mutex_lock (v4l2xv->mutex); + keysym = XKeycodeToKeysym (v4l2xv->dpy, e.xkey.keycode, 0); + if (keysym != NoSymbol) { + key_str = XKeysymToString (keysym); + } else { + key_str = "unknown"; + } + g_mutex_unlock (v4l2xv->mutex); + GST_DEBUG_OBJECT (v4l2object->element, + "key %d pressed over window at %d,%d (%s)", + e.xkey.keycode, e.xkey.x, e.xkey.y, key_str); + gst_navigation_send_key_event (GST_NAVIGATION (v4l2object->element), + e.type == KeyPress ? "key-press" : "key-release", key_str); + break; + default: + GST_DEBUG_OBJECT (v4l2object->element, + "unhandled X event (%d)", e.type); + } + + g_mutex_lock (v4l2xv->mutex); + } + } + + /* Handle ConfigureNotify */ + while (XCheckWindowEvent (v4l2xv->dpy, v4l2object->xwindow_id, + StructureNotifyMask, &e)) { + switch (e.type) { + case ConfigureNotify: + update_geometry (v4l2object); + break; + default: + break; + } + } + g_mutex_unlock (v4l2xv->mutex); + } + + /* repeat */ + return TRUE; +} + void gst_v4l2_xoverlay_set_window_handle (GstV4l2Object * v4l2object, guintptr id) { GstV4l2Xv *v4l2xv; - XWindowAttributes attr; XID xwindow_id = id; gboolean change = (v4l2object->xwindow_id != xwindow_id); @@ -235,13 +408,76 @@ gst_v4l2_xoverlay_set_window_handle (GstV4l2Object * v4l2object, guintptr id) XvSelectVideoNotify (v4l2xv->dpy, v4l2object->xwindow_id, 1); } - XGetWindowAttributes (v4l2xv->dpy, v4l2object->xwindow_id, &attr); - XvPutVideo (v4l2xv->dpy, v4l2xv->port, v4l2object->xwindow_id, - DefaultGC (v4l2xv->dpy, DefaultScreen (v4l2xv->dpy)), - 0, 0, attr.width, attr.height, 0, 0, attr.width, attr.height); + update_geometry (v4l2object); if (v4l2xv->idle_id) g_source_remove (v4l2xv->idle_id); v4l2xv->idle_id = g_idle_add (idle_refresh, v4l2object); g_mutex_unlock (v4l2xv->mutex); } + +/** + * gst_v4l2_xoverlay_prepare_xwindow_id: + * @param v4l2object + * @param required TRUE if display is required (ie. TRUE for v4l2sink, but + * FALSE for any other element with optional overlay capabilities) + */ +void +gst_v4l2_xoverlay_prepare_xwindow_id (GstV4l2Object * v4l2object, + gboolean required) +{ + if (!GST_V4L2_IS_OVERLAY (v4l2object)) + return; + + gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (v4l2object->element)); + + if (required && !v4l2object->xwindow_id) { + GstV4l2Xv *v4l2xv; + Window win; + int width, height; + long event_mask; + + if (!v4l2object->xv && GST_V4L2_IS_OPEN (v4l2object)) + gst_v4l2_xoverlay_open (v4l2object); + + v4l2xv = v4l2object->xv; + + /* if xoverlay is not supported, just bail */ + if (!v4l2xv) + return; + + /* xoverlay is supported, but we don't have a window.. so create one */ + GST_DEBUG_OBJECT (v4l2object->element, "creating window"); + + g_mutex_lock (v4l2xv->mutex); + + width = XDisplayWidth (v4l2xv->dpy, DefaultScreen (v4l2xv->dpy)); + height = XDisplayHeight (v4l2xv->dpy, DefaultScreen (v4l2xv->dpy)); + GST_DEBUG_OBJECT (v4l2object->element, "dpy=%p", v4l2xv->dpy); + + win = XCreateSimpleWindow (v4l2xv->dpy, + DefaultRootWindow (v4l2xv->dpy), + 0, 0, width, height, 0, 0, + XBlackPixel (v4l2xv->dpy, DefaultScreen (v4l2xv->dpy))); + + GST_DEBUG_OBJECT (v4l2object->element, "win=%lu", win); + + event_mask = ExposureMask | StructureNotifyMask; + if (GST_IS_NAVIGATION (v4l2object->element)) { + event_mask |= PointerMotionMask | + KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask; + } + XSelectInput (v4l2xv->dpy, win, event_mask); + v4l2xv->event_id = g_timeout_add (45, event_refresh, v4l2object); + + XMapRaised (v4l2xv->dpy, win); + + XSync (v4l2xv->dpy, FALSE); + + g_mutex_unlock (v4l2xv->mutex); + + GST_DEBUG_OBJECT (v4l2object->element, "got window"); + + gst_v4l2_xoverlay_set_window_handle (v4l2object, win); + } +} diff --git a/sys/v4l2/gstv4l2xoverlay.h b/sys/v4l2/gstv4l2xoverlay.h index bd77e8a2d1..1a09306836 100644 --- a/sys/v4l2/gstv4l2xoverlay.h +++ b/sys/v4l2/gstv4l2xoverlay.h @@ -28,6 +28,8 @@ #include #include +#include +#include /* for GstVideoRectange */ #include "gstv4l2object.h" @@ -35,10 +37,14 @@ G_BEGIN_DECLS void gst_v4l2_xoverlay_start (GstV4l2Object *v4l2object); void gst_v4l2_xoverlay_stop (GstV4l2Object *v4l2object); +gboolean gst_v4l2_xoverlay_get_render_rect (GstV4l2Object *v4l2object, + GstVideoRectangle *rect); void gst_v4l2_xoverlay_interface_init (GstXOverlayClass * klass); void gst_v4l2_xoverlay_set_window_handle (GstV4l2Object * v4l2object, guintptr id); +void gst_v4l2_xoverlay_prepare_xwindow_id (GstV4l2Object * v4l2object, + gboolean required); #define GST_IMPLEMENT_V4L2_XOVERLAY_METHODS(Type, interface_as_function) \ diff --git a/sys/v4l2/v4l2_calls.c b/sys/v4l2/v4l2_calls.c index f2c2c081d1..e9d306999f 100644 --- a/sys/v4l2/v4l2_calls.c +++ b/sys/v4l2/v4l2_calls.c @@ -46,7 +46,10 @@ #include "gstv4l2colorbalance.h" #include "gstv4l2src.h" + +#ifdef HAVE_EXPERIMENTAL #include "gstv4l2sink.h" +#endif #include "gst/gst-i18n-plugin.h" @@ -462,9 +465,11 @@ gst_v4l2_open (GstV4l2Object * v4l2object) !(v4l2object->vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) goto not_capture; +#ifdef HAVE_EXPERIMENTAL if (GST_IS_V4L2SINK (v4l2object->element) && !(v4l2object->vcap.capabilities & V4L2_CAP_VIDEO_OUTPUT)) goto not_output; +#endif /* create enumerations, posts errors. */ if (!gst_v4l2_fill_lists (v4l2object)) @@ -510,6 +515,7 @@ not_capture: ("Capabilities: 0x%x", v4l2object->vcap.capabilities)); goto error; } +#ifdef HAVE_EXPERIMENTAL not_output: { GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, NOT_FOUND, @@ -518,6 +524,7 @@ not_output: ("Capabilities: 0x%x", v4l2object->vcap.capabilities)); goto error; } +#endif error: { if (GST_V4L2_IS_OPEN (v4l2object)) { @@ -824,11 +831,14 @@ gst_v4l2_get_input (GstV4l2Object * v4l2object, gint * input) /* ERRORS */ input_failed: - { + if (v4l2object->vcap.capabilities & V4L2_CAP_TUNER) { + /* only give a warning message if driver actually claims to have tuner + * support + */ GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS, (_("Failed to get current input on device '%s'. May be it is a radio device"), v4l2object->videodev), GST_ERROR_SYSTEM); - return FALSE; } + return FALSE; } gboolean @@ -846,10 +856,70 @@ gst_v4l2_set_input (GstV4l2Object * v4l2object, gint input) /* ERRORS */ input_failed: - { + if (v4l2object->vcap.capabilities & V4L2_CAP_TUNER) { + /* only give a warning message if driver actually claims to have tuner + * support + */ GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS, (_("Failed to set input %d on device %s."), input, v4l2object->videodev), GST_ERROR_SYSTEM); - return FALSE; } + return FALSE; +} + +gboolean +gst_v4l2_get_output (GstV4l2Object * v4l2object, gint * output) +{ + gint n; + + GST_DEBUG_OBJECT (v4l2object->element, "trying to get output"); + + if (!GST_V4L2_IS_OPEN (v4l2object)) + return FALSE; + + if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_OUTPUT, &n) < 0) + goto output_failed; + + *output = n; + + GST_DEBUG_OBJECT (v4l2object->element, "output: %d", n); + + return TRUE; + + /* ERRORS */ +output_failed: + if (v4l2object->vcap.capabilities & V4L2_CAP_TUNER) { + /* only give a warning message if driver actually claims to have tuner + * support + */ + GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS, + (_("Failed to get current output on device '%s'. May be it is a radio device"), v4l2object->videodev), GST_ERROR_SYSTEM); + } + return FALSE; +} + +gboolean +gst_v4l2_set_output (GstV4l2Object * v4l2object, gint output) +{ + GST_DEBUG_OBJECT (v4l2object->element, "trying to set output to %d", output); + + if (!GST_V4L2_IS_OPEN (v4l2object)) + return FALSE; + + if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_S_OUTPUT, &output) < 0) + goto output_failed; + + return TRUE; + + /* ERRORS */ +output_failed: + if (v4l2object->vcap.capabilities & V4L2_CAP_TUNER) { + /* only give a warning message if driver actually claims to have tuner + * support + */ + GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS, + (_("Failed to set output %d on device %s."), + output, v4l2object->videodev), GST_ERROR_SYSTEM); + } + return FALSE; } diff --git a/sys/v4l2/v4l2_calls.h b/sys/v4l2/v4l2_calls.h index c5c1808a4e..a47872dd43 100644 --- a/sys/v4l2/v4l2_calls.h +++ b/sys/v4l2/v4l2_calls.h @@ -111,12 +111,10 @@ gboolean gst_v4l2_get_input (GstV4l2Object * v4l2object, gint * input); gboolean gst_v4l2_set_input (GstV4l2Object * v4l2object, gint input); -#if 0 /* output not handled by now */ gboolean gst_v4l2_get_output (GstV4l2Object *v4l2object, gint *output); gboolean gst_v4l2_set_output (GstV4l2Object *v4l2object, gint output); -#endif /* #if 0 - output not handled by now */ /* frequency control */ gboolean gst_v4l2_get_frequency (GstV4l2Object *v4l2object, diff --git a/sys/v4l2/v4l2src_calls.c b/sys/v4l2/v4l2src_calls.c index e33fa618a8..fb6c37456b 100644 --- a/sys/v4l2/v4l2src_calls.c +++ b/sys/v4l2/v4l2src_calls.c @@ -68,7 +68,7 @@ gst_v4l2src_buffer_pool_activate (GstV4l2BufferPool * pool, { GstV4l2Buffer *buf; - while ((buf = gst_v4l2_buffer_pool_get (pool)) != NULL) + while ((buf = gst_v4l2_buffer_pool_get (pool, FALSE)) != NULL) if (!gst_v4l2_buffer_pool_qbuf (pool, buf)) goto queue_failed; @@ -214,7 +214,8 @@ too_many_trials: ******************************************************/ gboolean gst_v4l2src_set_capture (GstV4l2Src * v4l2src, guint32 pixelformat, - guint32 width, guint32 height, guint fps_n, guint fps_d) + guint32 width, guint32 height, gboolean interlaced, + guint fps_n, guint fps_d) { gint fd = v4l2src->v4l2object->video_fd; struct v4l2_streamparm stream; @@ -223,7 +224,7 @@ gst_v4l2src_set_capture (GstV4l2Src * v4l2src, guint32 pixelformat, return TRUE; if (!gst_v4l2_object_set_format (v4l2src->v4l2object, pixelformat, width, - height)) { + height, interlaced)) { /* error already reported */ return FALSE; } diff --git a/sys/v4l2/v4l2src_calls.h b/sys/v4l2/v4l2src_calls.h index 1fc7411fae..709191870e 100644 --- a/sys/v4l2/v4l2src_calls.h +++ b/sys/v4l2/v4l2src_calls.h @@ -31,7 +31,8 @@ gboolean gst_v4l2src_get_capture (GstV4l2Src * v4l2src); gboolean gst_v4l2src_set_capture (GstV4l2Src * v4l2src, guint32 pixelformat, guint32 width, guint32 height, - guint32 fps_n, guint32 fps_d); + gboolean interlaced, + guint32 fps_n, guint32 fps_d); gboolean gst_v4l2src_capture_init (GstV4l2Src * v4l2src, GstCaps *caps); gboolean gst_v4l2src_capture_start (GstV4l2Src * v4l2src); diff --git a/sys/ximage/gstximagesrc.c b/sys/ximage/gstximagesrc.c index e525fbbc35..e705ccbe32 100644 --- a/sys/ximage/gstximagesrc.c +++ b/sys/ximage/gstximagesrc.c @@ -69,7 +69,8 @@ enum PROP_STARTX, PROP_STARTY, PROP_ENDX, - PROP_ENDY + PROP_ENDY, + PROP_REMOTE, }; GST_BOILERPLATE (GstXImageSrc, gst_ximage_src, GstPushSrc, GST_TYPE_PUSH_SRC); @@ -465,10 +466,10 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc) if (ximagesrc->endx > ximagesrc->startx && ximagesrc->endy > ximagesrc->starty) { /* see if damage area intersects */ - if (rects[i].x + rects[i].width < ximagesrc->startx || + if (rects[i].x + rects[i].width - 1 < ximagesrc->startx || rects[i].x > ximagesrc->endx) { /* trivial reject */ - } else if (rects[i].y + rects[i].height < ximagesrc->starty || + } else if (rects[i].y + rects[i].height - 1 < ximagesrc->starty || rects[i].y > ximagesrc->endy) { /* trivial reject */ } else { @@ -479,12 +480,12 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc) rects[i].x; starty = (rects[i].y < ximagesrc->starty) ? ximagesrc->starty : rects[i].y; - width = (rects[i].x + rects[i].width < ximagesrc->endx) ? + width = (rects[i].x + rects[i].width - 1 < ximagesrc->endx) ? rects[i].x + rects[i].width - startx : - ximagesrc->endx - startx; - height = (rects[i].y + rects[i].height < ximagesrc->endy) ? + ximagesrc->endx - startx + 1; + height = (rects[i].y + rects[i].height - 1 < ximagesrc->endy) ? rects[i].y + rects[i].height - starty : ximagesrc->endy - - starty; + starty + 1; GST_LOG_OBJECT (ximagesrc, "Retrieving damaged sub-region @ %d,%d size %dx%d as intersect region", @@ -591,9 +592,16 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc) #endif /* HAVE_XSHM */ { GST_DEBUG_OBJECT (ximagesrc, "Retrieving screen using XGetImage"); - ximage->ximage = XGetImage (ximagesrc->xcontext->disp, ximagesrc->xwindow, - ximagesrc->startx, ximagesrc->starty, ximagesrc->width, - ximagesrc->height, AllPlanes, ZPixmap); + if (ximagesrc->remote) { + XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow, + ximagesrc->startx, ximagesrc->starty, ximagesrc->width, + ximagesrc->height, AllPlanes, ZPixmap, ximage->ximage, 0, 0); + } else { + ximage->ximage = + XGetImage (ximagesrc->xcontext->disp, ximagesrc->xwindow, + ximagesrc->startx, ximagesrc->starty, ximagesrc->width, + ximagesrc->height, AllPlanes, ZPixmap); + } } #ifdef HAVE_XDAMAGE } @@ -824,6 +832,9 @@ gst_ximage_src_set_property (GObject * object, guint prop_id, case PROP_ENDY: src->endy = g_value_get_uint (value); break; + case PROP_REMOTE: + src->remote = g_value_get_boolean (value); + break; default: break; } @@ -864,6 +875,9 @@ gst_ximage_src_get_property (GObject * object, guint prop_id, GValue * value, case PROP_ENDY: g_value_set_uint (value, src->endy); break; + case PROP_REMOTE: + g_value_set_boolean (value, src->remote); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -943,27 +957,36 @@ gst_ximage_src_get_caps (GstBaseSrc * bs) width = xcontext->width; height = xcontext->height; - if (s->endx > s->startx && s->endy > s->starty) { + + /* property comments say 0 means right/bottom, means we can't capture + the top left pixel alone */ + if (s->endx == 0) + s->endx = width - 1; + if (s->endy == 0) + s->endy = height - 1; + + if (s->endx >= s->startx && s->endy >= s->starty) { /* this means user has put in values */ if (s->startx < xcontext->width && s->endx < xcontext->width && - s->starty < xcontext->height && s->endy < xcontext->height) { + s->starty < xcontext->height && s->endy < xcontext->height && + s->startx >= 0 && s->starty >= 0) { /* values are fine */ - s->width = width = s->endx - s->startx; - s->height = height = s->endy - s->starty; + s->width = width = s->endx - s->startx + 1; + s->height = height = s->endy - s->starty + 1; } else { GST_WARNING ("User put in co-ordinates overshooting the X resolution, setting to full screen"); s->startx = 0; s->starty = 0; - s->endx = 0; - s->endy = 0; + s->endx = width - 1; + s->endy = height - 1; } } else { GST_WARNING ("User put in bogus co-ordinates, setting to full screen"); s->startx = 0; s->starty = 0; - s->endx = 0; - s->endy = 0; + s->endx = width - 1; + s->endy = height - 1; } GST_DEBUG ("width = %d, height=%d", width, height); return gst_caps_new_simple ("video/x-raw-rgb", @@ -1101,6 +1124,19 @@ gst_ximage_src_class_init (GstXImageSrcClass * klass) "Y coordinate of bottom right corner of area to be recorded (0 for bottom right of screen)", 0, G_MAXINT, 0, G_PARAM_READWRITE)); + /** + * GstXImageSrc:remote + * + * Whether the X display is remote. The element will try to use alternate calls + * known to work better with remote displays. + * + * Since: 0.10.26 + **/ + g_object_class_install_property (gc, PROP_REMOTE, + g_param_spec_boolean ("remote", "Remote dispay", + "Whether the display is remote", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + parent_class = g_type_class_peek_parent (klass); push_class->create = gst_ximage_src_create; @@ -1127,6 +1163,7 @@ gst_ximage_src_init (GstXImageSrc * ximagesrc, GstXImageSrcClass * klass) ximagesrc->starty = 0; ximagesrc->endx = 0; ximagesrc->endy = 0; + ximagesrc->remote = FALSE; } static gboolean diff --git a/sys/ximage/gstximagesrc.h b/sys/ximage/gstximagesrc.h index f223d1e761..f436df68fb 100644 --- a/sys/ximage/gstximagesrc.h +++ b/sys/ximage/gstximagesrc.h @@ -83,6 +83,9 @@ struct _GstXImageSrc guint endx; guint endy; + /* whether to use remote friendly calls */ + gboolean remote; + #ifdef HAVE_XFIXES int fixes_event_base; XFixesCursorImage *cursor_image; diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 059eedaefe..ccce33cf03 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -164,7 +164,8 @@ noinst_PROGRAMS = \ elements/autodetect AM_CFLAGS = $(GST_OBJ_CFLAGS) $(GST_CHECK_CFLAGS) $(CHECK_CFLAGS) \ - $(GST_OPTION_CFLAGS) -DGST_TEST_FILES_PATH="\"$(TEST_FILES_DIRECTORY)\"" + $(GST_OPTION_CFLAGS) -DGST_TEST_FILES_PATH="\"$(TEST_FILES_DIRECTORY)\"" \ + -UG_DISABLE_ASSERT -UG_DISABLE_CAST_CHECKS LDADD = $(GST_OBJ_LIBS) $(GST_CHECK_LIBS) $(CHECK_LIBS) # valgrind testing @@ -202,12 +203,15 @@ elements_interleave_LDADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR elements_imagefreeze_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(AM_CFLAGS) elements_imagefreeze_LDADD = $(GST_BASE_LIBS) $(LDADD) -lgstvideo-$(GST_MAJORMINOR) +elements_jpegenc_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(AM_CFLAGS) +elements_jpegenc_LDADD = $(GST_BASE_LIBS) $(LDADD) -lgstapp-0.10 + elements_level_LDADD = $(LDADD) $(LIBM) elements_matroskamux_LDADD = $(GST_BASE_LIBS) $(LDADD) $(LIBM) elements_rtpbin_buffer_list_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \ - $(WARNING_CFLAGS) $(ERROR_CFLAGS) $(GST_CHECK_CFLAGS) + $(WARNING_CFLAGS) $(ERROR_CFLAGS) $(GST_CHECK_CFLAGS) $(AM_CFLAGS) elements_rtpbin_buffer_list_LDADD = $(GST_PLUGINS_BASE_LIBS) \ -lgstnetbuffer-@GST_MAJORMINOR@ -lgstrtp-@GST_MAJORMINOR@ \ $(GST_BASE_LIBS) $(GST_LIBS_LIBS) $(GST_CHECK_LIBS) diff --git a/tests/check/elements/deinterlace.c b/tests/check/elements/deinterlace.c index 6606a5f7a1..b111c86f74 100644 --- a/tests/check/elements/deinterlace.c +++ b/tests/check/elements/deinterlace.c @@ -53,6 +53,9 @@ GST_END_TEST; #define CAPS_VIDEO_COMMON \ "width=(int)800, height=(int)600, framerate=(fraction)15/1" +#define CAPS_IMAGE_COMMON \ + "width=(int)3200, height=(int)3400, framerate=(fraction)0/1" + #define CAPS_YUY2 \ "video/x-raw-yuv, " \ CAPS_VIDEO_COMMON ", " \ @@ -71,6 +74,24 @@ GST_END_TEST; CAPS_YVYU ", " \ "interlaced=(boolean)true" +#define CAPS_YUY2_IMAGE \ + "video/x-raw-yuv, " \ + CAPS_IMAGE_COMMON ", " \ + "format=(fourcc)YUY2" + +#define CAPS_YUY2_INTERLACED_IMAGE \ + CAPS_YUY2_IMAGE ", " \ + "interlaced=(boolean)true" + +#define CAPS_YVYU_IMAGE \ + "video/x-raw-yuv, " \ + CAPS_IMAGE_COMMON ", " \ + "format=(fourcc)YVYU" + +#define CAPS_YVYU_INTERLACED_IMAGE \ + CAPS_YVYU_IMAGE ", " \ + "interlaced=(boolean)true" + static GstElement *deinterlace; static GstPad *srcpad; static GstPad *sinkpad; @@ -295,10 +316,14 @@ GST_START_TEST (test_mode_auto_accept_caps) /* try to set non interlaced caps */ deinterlace_set_string_caps_and_check (CAPS_YVYU, FALSE); deinterlace_set_string_caps_and_check (CAPS_YUY2, FALSE); + deinterlace_set_string_caps_and_check (CAPS_YVYU_IMAGE, FALSE); + deinterlace_set_string_caps_and_check (CAPS_YUY2_IMAGE, FALSE); /* now try to set interlaced caps */ deinterlace_set_string_caps_and_check (CAPS_YVYU_INTERLACED, TRUE); deinterlace_set_string_caps_and_check (CAPS_YUY2_INTERLACED, TRUE); + deinterlace_set_string_caps_and_check (CAPS_YVYU_INTERLACED_IMAGE, TRUE); + deinterlace_set_string_caps_and_check (CAPS_YUY2_INTERLACED_IMAGE, TRUE); /* cleanup */ gst_object_unref (sinkpad); @@ -322,10 +347,14 @@ GST_START_TEST (test_mode_forced_accept_caps) /* try to set non interlaced caps */ deinterlace_set_string_caps_and_check (CAPS_YVYU, TRUE); deinterlace_set_string_caps_and_check (CAPS_YUY2, TRUE); + deinterlace_set_string_caps_and_check (CAPS_YVYU_IMAGE, TRUE); + deinterlace_set_string_caps_and_check (CAPS_YUY2_IMAGE, TRUE); /* now try to set interlaced caps */ deinterlace_set_string_caps_and_check (CAPS_YVYU_INTERLACED, TRUE); deinterlace_set_string_caps_and_check (CAPS_YUY2_INTERLACED, TRUE); + deinterlace_set_string_caps_and_check (CAPS_YVYU_INTERLACED_IMAGE, TRUE); + deinterlace_set_string_caps_and_check (CAPS_YUY2_INTERLACED_IMAGE, TRUE); /* cleanup */ gst_object_unref (sinkpad); @@ -349,10 +378,14 @@ GST_START_TEST (test_mode_disabled_accept_caps) /* try to set non interlaced caps */ deinterlace_set_string_caps_and_check (CAPS_YVYU, FALSE); deinterlace_set_string_caps_and_check (CAPS_YUY2, FALSE); + deinterlace_set_string_caps_and_check (CAPS_YVYU_IMAGE, FALSE); + deinterlace_set_string_caps_and_check (CAPS_YUY2_IMAGE, FALSE); /* now try to set interlaced caps */ deinterlace_set_string_caps_and_check (CAPS_YVYU_INTERLACED, FALSE); deinterlace_set_string_caps_and_check (CAPS_YUY2_INTERLACED, FALSE); + deinterlace_set_string_caps_and_check (CAPS_YVYU_INTERLACED_IMAGE, FALSE); + deinterlace_set_string_caps_and_check (CAPS_YUY2_INTERLACED_IMAGE, FALSE); /* cleanup */ gst_object_unref (sinkpad); @@ -371,6 +404,11 @@ GST_START_TEST (test_mode_disabled_passthrough) deinterlace_check_passthrough (2, CAPS_YVYU_INTERLACED); deinterlace_check_passthrough (2, CAPS_YUY2); deinterlace_check_passthrough (2, CAPS_YVYU); + + deinterlace_check_passthrough (2, CAPS_YUY2_INTERLACED_IMAGE); + deinterlace_check_passthrough (2, CAPS_YVYU_INTERLACED_IMAGE); + deinterlace_check_passthrough (2, CAPS_YUY2_IMAGE); + deinterlace_check_passthrough (2, CAPS_YVYU_IMAGE); } GST_END_TEST; @@ -380,6 +418,8 @@ GST_START_TEST (test_mode_auto_deinterlaced_passthrough) /* 0 is auto mode */ deinterlace_check_passthrough (0, CAPS_YUY2); deinterlace_check_passthrough (0, CAPS_YVYU); + deinterlace_check_passthrough (0, CAPS_YUY2_IMAGE); + deinterlace_check_passthrough (0, CAPS_YVYU_IMAGE); } GST_END_TEST; diff --git a/tests/check/elements/jpegenc.c b/tests/check/elements/jpegenc.c index 674617d6bc..2c992f77b6 100644 --- a/tests/check/elements/jpegenc.c +++ b/tests/check/elements/jpegenc.c @@ -23,11 +23,13 @@ #include #include +#include /* For ease of programming we use globals to keep refs for our floating * sink pads we create; otherwise we always have to do get_pad, * get_peer, and then remove references in every test function */ static GstPad *mysinkpad; +static GstPad *mysrcpad; #define JPEG_CAPS_STRING "image/jpeg" @@ -52,6 +54,12 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_ALWAYS, GST_STATIC_CAPS (JPEG_CAPS_RESTRICTIVE)); +static GstStaticPadTemplate any_srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + + static GstElement * setup_jpegenc (GstStaticPadTemplate * sinktemplate) { @@ -60,6 +68,8 @@ setup_jpegenc (GstStaticPadTemplate * sinktemplate) GST_DEBUG ("setup_jpegenc"); jpegenc = gst_check_setup_element ("jpegenc"); mysinkpad = gst_check_setup_sink_pad (jpegenc, sinktemplate, NULL); + mysrcpad = gst_check_setup_src_pad (jpegenc, &any_srctemplate, NULL); + gst_pad_set_active (mysrcpad, TRUE); gst_pad_set_active (mysinkpad, TRUE); return jpegenc; @@ -71,11 +81,44 @@ cleanup_jpegenc (GstElement * jpegenc) GST_DEBUG ("cleanup_jpegenc"); gst_element_set_state (jpegenc, GST_STATE_NULL); + gst_pad_set_active (mysrcpad, FALSE); gst_pad_set_active (mysinkpad, FALSE); gst_check_teardown_sink_pad (jpegenc); + gst_check_teardown_src_pad (jpegenc); gst_check_teardown_element (jpegenc); } +static GstBuffer * +create_video_buffer (GstCaps * caps) +{ + GstElement *pipeline; + GstElement *cf; + GstElement *sink; + GstBuffer *buffer; + + pipeline = + gst_parse_launch + ("videotestsrc num-buffers=1 ! capsfilter name=cf ! appsink name=sink", + NULL); + g_assert (pipeline != NULL); + + cf = gst_bin_get_by_name (GST_BIN (pipeline), "cf"); + sink = gst_bin_get_by_name (GST_BIN (pipeline), "sink"); + + g_object_set (G_OBJECT (cf), "caps", caps, NULL); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + buffer = gst_app_sink_pull_buffer (GST_APP_SINK (sink)); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + gst_object_unref (sink); + gst_object_unref (cf); + return buffer; +} + + GST_START_TEST (test_jpegenc_getcaps) { GstElement *jpegenc; @@ -129,6 +172,50 @@ GST_START_TEST (test_jpegenc_getcaps) GST_END_TEST; + +GST_START_TEST (test_jpegenc_different_caps) +{ + GstElement *jpegenc; + GstBuffer *buffer; + GstCaps *caps; + GstCaps *allowed_caps; + + /* now use a more restricted one and check the resulting caps */ + jpegenc = setup_jpegenc (&any_sinktemplate); + gst_element_set_state (jpegenc, GST_STATE_PLAYING); + + /* push first buffer with 800x600 resolution */ + caps = gst_caps_new_simple ("video/x-raw-yuv", "width", G_TYPE_INT, + 800, "height", G_TYPE_INT, 600, "framerate", + GST_TYPE_FRACTION, 1, 1, "format", GST_TYPE_FOURCC, + GST_MAKE_FOURCC ('I', '4', '2', '0'), NULL); + buffer = create_video_buffer (caps); + gst_caps_unref (caps); + fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); + + /* check the allowed caps to see if a second buffer with a different + * caps could be negotiated */ + allowed_caps = gst_pad_get_allowed_caps (mysrcpad); + + /* the caps we want to negotiate to */ + caps = gst_caps_new_simple ("video/x-raw-yuv", "width", G_TYPE_INT, + 640, "height", G_TYPE_INT, 480, "framerate", + GST_TYPE_FRACTION, 1, 1, "format", GST_TYPE_FOURCC, + GST_MAKE_FOURCC ('I', '4', '2', '0'), NULL); + fail_unless (gst_caps_can_intersect (allowed_caps, caps)); + + /* push second buffer with 640x480 resolution */ + buffer = create_video_buffer (caps); + gst_caps_unref (caps); + fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); + + gst_caps_unref (allowed_caps); + gst_element_set_state (jpegenc, GST_STATE_NULL); + cleanup_jpegenc (jpegenc); +} + +GST_END_TEST; + static Suite * jpegenc_suite (void) { @@ -137,6 +224,7 @@ jpegenc_suite (void) suite_add_tcase (s, tc_chain); tcase_add_test (tc_chain, test_jpegenc_getcaps); + tcase_add_test (tc_chain, test_jpegenc_different_caps); return s; } diff --git a/tests/check/elements/matroskamux.c b/tests/check/elements/matroskamux.c index 9d6f3752d3..bbc1862e38 100644 --- a/tests/check/elements/matroskamux.c +++ b/tests/check/elements/matroskamux.c @@ -387,6 +387,56 @@ GST_START_TEST (test_block_group) GST_END_TEST; +GST_START_TEST (test_reset) +{ + GstElement *matroskamux; + GstBuffer *inbuffer; + GstBuffer *outbuffer; + int num_buffers; + int i; + + matroskamux = setup_matroskamux (&srcac3template); + fail_unless (gst_element_set_state (matroskamux, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "could not set to playing"); + + inbuffer = gst_buffer_new_and_alloc (1); + ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); + fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK); + num_buffers = g_list_length (buffers); + fail_unless (num_buffers >= 1, + "expected at least 1 buffer, but got only %d", num_buffers); + + fail_unless (gst_element_set_state (matroskamux, + GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null"); + + fail_unless (gst_element_set_state (matroskamux, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "could not set to playing"); + + inbuffer = gst_buffer_new_and_alloc (1); + ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); + fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK); + num_buffers = g_list_length (buffers); + fail_unless (num_buffers >= 2, + "expected at least 2 buffers, but got only %d", num_buffers); + + for (i = 0; i < num_buffers; ++i) { + outbuffer = GST_BUFFER (buffers->data); + fail_if (outbuffer == NULL); + buffers = g_list_remove (buffers, outbuffer); + + ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1); + gst_buffer_unref (outbuffer); + } + + cleanup_matroskamux (matroskamux); + g_list_free (buffers); + buffers = NULL; +} + +GST_END_TEST; + static Suite * matroskamux_suite (void) { @@ -397,6 +447,7 @@ matroskamux_suite (void) tcase_add_test (tc_chain, test_ebml_header); tcase_add_test (tc_chain, test_vorbis_header); tcase_add_test (tc_chain, test_block_group); + tcase_add_test (tc_chain, test_reset); return s; } diff --git a/tests/check/pipelines/wavenc.c b/tests/check/pipelines/wavenc.c index 1db4635dce..ee5a3ffc00 100644 --- a/tests/check/pipelines/wavenc.c +++ b/tests/check/pipelines/wavenc.c @@ -111,6 +111,7 @@ make_n_channel_wav (const gint channels, const GValueArray * arr) g_main_loop_run (loop); gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); g_free (audiotestsrc); } @@ -129,6 +130,7 @@ GST_START_TEST (test_encode_stereo) g_value_unset (&val); make_n_channel_wav (2, arr); + g_value_array_free (arr); } GST_END_TEST; diff --git a/tests/examples/Makefile.am b/tests/examples/Makefile.am index d3d93d4af3..54e53bafb7 100644 --- a/tests/examples/Makefile.am +++ b/tests/examples/Makefile.am @@ -1,5 +1,11 @@ -SUBDIRS = audiofx equalizer level pulse rtp shapewipe spectrum v4l2 +if USE_JACK +JACK_DIR=jack +else +JACK_DIR= +endif -DIST_SUBDIRS = audiofx equalizer level pulse rtp shapewipe spectrum v4l2 +SUBDIRS = audiofx equalizer $(JACK_DIR) level pulse rtp shapewipe spectrum v4l2 + +DIST_SUBDIRS = audiofx equalizer jack level pulse rtp shapewipe spectrum v4l2 include $(top_srcdir)/common/parallel-subdirs.mak diff --git a/tests/examples/audiofx/firfilter-example.c b/tests/examples/audiofx/firfilter-example.c index a0c31578d5..b344e74e63 100644 --- a/tests/examples/audiofx/firfilter-example.c +++ b/tests/examples/audiofx/firfilter-example.c @@ -90,7 +90,7 @@ on_rate_changed (GstElement * element, gint rate, gpointer user_data) * a better result than given from the rectangular window */ for (i = 0; i < 32; i++) - filter_kernel[i] *= (0.54 - 0.46 * cos (2 * M_PI * i / 32)); + filter_kernel[i] *= (0.54 - 0.46 * cos (2 * G_PI * i / 32)); va = g_value_array_new (1); diff --git a/tests/examples/audiofx/iirfilter-example.c b/tests/examples/audiofx/iirfilter-example.c index 8ccffb7433..7fac2ac92b 100644 --- a/tests/examples/audiofx/iirfilter-example.c +++ b/tests/examples/audiofx/iirfilter-example.c @@ -63,7 +63,7 @@ on_rate_changed (GstElement * element, gint rate, gpointer user_data) gdouble x; if (rate / 2.0 > CUTOFF) - x = exp (-2.0 * M_PI * (CUTOFF / rate)); + x = exp (-2.0 * G_PI * (CUTOFF / rate)); else x = 0.0; diff --git a/tests/examples/jack/Makefile.am b/tests/examples/jack/Makefile.am new file mode 100644 index 0000000000..ad61cbd876 --- /dev/null +++ b/tests/examples/jack/Makefile.am @@ -0,0 +1,12 @@ +if HAVE_GTK +GTK_EXAMPLES=jack_client +else +GTK_EXAMPLES= +endif + +noinst_PROGRAMS = $(GTK_EXAMPLES) + +jack_client_SOURCES = jack_client.c +jack_client_CFLAGS = $(GST_CFLAGS) $(GTK_CFLAGS) $(JACK_CFLAGS) +jack_client_LDFLAGS = $(GST_LIBS) $(GTK_LIBS) $(JACK_LIBS) + diff --git a/tests/examples/jack/jack_client.c b/tests/examples/jack/jack_client.c new file mode 100644 index 0000000000..99599ab5cb --- /dev/null +++ b/tests/examples/jack/jack_client.c @@ -0,0 +1,79 @@ +/* This app demonstrates the creation and use of a jack client in conjunction + * with the jack plugins. This way, an application can control the jack client + * directly. + */ + +#include +#include +#include + +static gboolean +quit_cb (gpointer data) +{ + gtk_main_quit (); + return FALSE; +} + +int +main (int argc, char **argv) +{ + jack_client_t *src_client, *sink_client; + jack_status_t status; + GstElement *pipeline, *src, *sink; + GstStateChangeReturn ret; + + gst_init (&argc, &argv); + + /* create jack clients */ + src_client = jack_client_open ("src_client", JackNoStartServer, &status); + if (src_client == NULL) { + if (status & JackServerFailed) + g_print ("JACK server not running\n"); + else + g_print ("jack_client_open() failed, status = 0x%2.0x\n", status); + return 1; + } + + sink_client = jack_client_open ("sink_client", JackNoStartServer, &status); + if (sink_client == NULL) { + if (status & JackServerFailed) + g_print ("JACK server not running\n"); + else + g_print ("jack_client_open() failed, status = 0x%2.0x\n", status); + return 1; + } + + /* create gst elements */ + pipeline = gst_pipeline_new ("my_pipeline"); + + src = gst_element_factory_make ("jackaudiosrc", NULL); + sink = gst_element_factory_make ("jackaudiosink", NULL); + + g_object_set (src, "client", src_client, NULL); + g_object_set (sink, "client", sink_client, NULL); + + gst_bin_add_many (GST_BIN (pipeline), src, sink, NULL); + + /* link everything together */ + if (!gst_element_link (src, sink)) { + g_print ("Failed to link elements!\n"); + return 1; + } + + /* run */ + ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) { + g_print ("Failed to start up pipeline!\n"); + return 1; + } + + /* quit after 5 seconds */ + g_timeout_add (5000, (GSourceFunc) quit_cb, NULL); + gtk_main (); + + /* clean up */ + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + + return 0; +} diff --git a/tests/examples/rtp/client-H263p-AMR.sh b/tests/examples/rtp/client-H263p-AMR.sh index 07a466fa81..bb0b795fee 100755 --- a/tests/examples/rtp/client-H263p-AMR.sh +++ b/tests/examples/rtp/client-H263p-AMR.sh @@ -10,7 +10,7 @@ VIDEO_DEC="rtph263pdepay ! ffdec_h263" AUDIO_DEC="rtpamrdepay ! amrnbdec" VIDEO_SINK="ffmpegcolorspace ! autovideosink" -AUDIO_SINK="audioconvert ! audioresample ! autoaudisink" +AUDIO_SINK="audioconvert ! audioresample ! autoaudiosink" gst-launch -v gstrtpbin name=rtpbin latency=100 \ udpsrc caps=$VIDEO_CAPS port=5000 ! rtpbin.recv_rtp_sink_0 \ diff --git a/tests/examples/rtp/client-H263p-PCMA.sh b/tests/examples/rtp/client-H263p-PCMA.sh index 8204c1f11d..9db0d7ecf0 100755 --- a/tests/examples/rtp/client-H263p-PCMA.sh +++ b/tests/examples/rtp/client-H263p-PCMA.sh @@ -13,7 +13,7 @@ VIDEO_DEC="rtph263pdepay ! ffdec_h263" AUDIO_DEC="rtppcmadepay ! alawdec" VIDEO_SINK="ffmpegcolorspace ! autovideosink" -AUDIO_SINK="audioconvert ! audioresample ! autoaudisink" +AUDIO_SINK="audioconvert ! audioresample ! autoaudiosink" LATENCY=100 diff --git a/tests/examples/rtp/client-H264-PCMA.sh b/tests/examples/rtp/client-H264-PCMA.sh index 600cefe935..7b104ab2d9 100755 --- a/tests/examples/rtp/client-H264-PCMA.sh +++ b/tests/examples/rtp/client-H264-PCMA.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# A simple RTP receiver +# A simple RTP receiver # # receives H264 encoded RTP video on port 5000, RTCP is received on port 5001. # the receiver RTCP reports are sent to port 5005 @@ -11,27 +11,27 @@ # RTP |udpsrc | | rtpbin | |h264depay| |h264dec| |xvimagesink| # port=5000 | src->recv_rtp recv_rtp->sink src->sink src->sink | # '-------' | | '---------' '-------' '-----------' -# | | +# | | # | | .-------. # | | |udpsink| RTCP # | send_rtcp->sink | port=5005 # .-------. | | '-------' sync=false # RTCP |udpsrc | | | async=false -# port=5001 | src->recv_rtcp | -# '-------' | | +# port=5001 | src->recv_rtcp | +# '-------' | | # | | -# .-------. | | .---------. .-------. .--------. +# .-------. | | .---------. .-------. .-------------. # RTP |udpsrc | | rtpbin | |pcmadepay| |alawdec| |autoaudiosink| -# port=5002 | src->recv_rtp recv_rtp->sink src->sink src->sink | -# '-------' | | '---------' '-------' '--------' -# | | +# port=5002 | src->recv_rtp recv_rtp->sink src->sink src->sink | +# '-------' | | '---------' '-------' '-------------' +# | | # | | .-------. # | | |udpsink| RTCP # | send_rtcp->sink | port=5007 # .-------. | | '-------' sync=false # RTCP |udpsrc | | | async=false -# port=5003 | src->recv_rtcp | -# '-------' '----------' +# port=5003 | src->recv_rtcp | +# '-------' '----------' # the destination machine to send RTCP to. This is the address of the sender and # is used to send back the RTCP reports of this receiver. If the data is sent @@ -51,7 +51,7 @@ VIDEO_DEC="rtph264depay ! ffdec_h264" AUDIO_DEC="rtppcmadepay ! alawdec" VIDEO_SINK="ffmpegcolorspace ! autovideosink" -AUDIO_SINK="audioconvert ! audioresample ! autoaudisink" +AUDIO_SINK="audioconvert ! audioresample ! autoaudiosink" gst-launch -v gstrtpbin name=rtpbin latency=$LATENCY \ udpsrc caps=$VIDEO_CAPS port=5000 ! rtpbin.recv_rtp_sink_0 \ diff --git a/tests/examples/rtp/client-PCMA.c b/tests/examples/rtp/client-PCMA.c index 0c895a2397..885b8264e2 100644 --- a/tests/examples/rtp/client-PCMA.c +++ b/tests/examples/rtp/client-PCMA.c @@ -55,6 +55,48 @@ * from another machine, change this address. */ #define DEST_HOST "127.0.0.1" +/* print the stats of a source */ +static void +print_source_stats (GObject * source) +{ + GstStructure *stats; + gchar *str; + + g_return_if_fail (source != NULL); + + /* get the source stats */ + g_object_get (source, "stats", &stats, NULL); + + /* simply dump the stats structure */ + str = gst_structure_to_string (stats); + g_print ("source stats: %s\n", str); + + gst_structure_free (stats); + g_free (str); +} + +/* will be called when gstrtpbin signals on-ssrc-active. It means that an RTCP + * packet was received from another source. */ +static void +on_ssrc_active_cb (GstElement * rtpbin, guint sessid, guint ssrc, + GstElement * depay) +{ + GObject *session, *isrc, *osrc; + + g_print ("got RTCP from session %u, SSRC %u\n", sessid, ssrc); + + /* get the right session */ + g_signal_emit_by_name (rtpbin, "get-internal-session", sessid, &session); + + /* get the internal source (the SSRC allocated to us, the receiver */ + g_object_get (session, "internal-source", &isrc, NULL); + print_source_stats (isrc); + + /* get the remote source that sent us RTCP */ + g_signal_emit_by_name (session, "get-source-by-ssrc", ssrc, &osrc); + print_source_stats (osrc); +} + /* will be called when rtpbin has validated a payload that we can depayload */ static void pad_added_cb (GstElement * rtpbin, GstPad * new_pad, GstElement * depay) @@ -174,6 +216,10 @@ main (int argc, char *argv[]) * user_data so that we can link to it. */ g_signal_connect (rtpbin, "pad-added", G_CALLBACK (pad_added_cb), audiodepay); + /* give some stats when we receive RTCP */ + g_signal_connect (rtpbin, "on-ssrc-active", G_CALLBACK (on_ssrc_active_cb), + audiodepay); + /* set the pipeline to playing */ g_print ("starting receiver pipeline\n"); gst_element_set_state (pipeline, GST_STATE_PLAYING); diff --git a/tests/examples/rtp/client-PCMA.sh b/tests/examples/rtp/client-PCMA.sh index 5d473404bd..aad6502e43 100755 --- a/tests/examples/rtp/client-PCMA.sh +++ b/tests/examples/rtp/client-PCMA.sh @@ -1,22 +1,22 @@ #!/bin/sh # -# A simple RTP receiver +# A simple RTP receiver # # receives alaw encoded RTP audio on port 5002, RTCP is received on port 5003. # the receiver RTCP reports are sent to port 5007 # -# .-------. .----------. .---------. .-------. .--------. +# .-------. .----------. .---------. .-------. .-------------. # RTP |udpsrc | | rtpbin | |pcmadepay| |alawdec| |autoaudiosink| -# port=5002 | src->recv_rtp recv_rtp->sink src->sink src->sink | -# '-------' | | '---------' '-------' '--------' -# | | +# port=5002 | src->recv_rtp recv_rtp->sink src->sink src->sink | +# '-------' | | '---------' '-------' '-------------' +# | | # | | .-------. # | | |udpsink| RTCP # | send_rtcp->sink | port=5007 # .-------. | | '-------' sync=false # RTCP |udpsrc | | | async=false -# port=5003 | src->recv_rtcp | -# '-------' '----------' +# port=5003 | src->recv_rtcp | +# '-------' '----------' # the caps of the sender RTP stream. This is usually negotiated out of band with @@ -25,7 +25,7 @@ AUDIO_CAPS="application/x-rtp,media=(string)audio,clock-rate=(int)8000,encoding- AUDIO_DEC="rtppcmadepay ! alawdec" -AUDIO_SINK="audioconvert ! audioresample ! autoaudisink" +AUDIO_SINK="audioconvert ! audioresample ! autoaudiosink" # the destination machine to send RTCP to. This is the address of the sender and # is used to send back the RTCP reports of this receiver. If the data is sent diff --git a/tests/examples/rtp/server-alsasrc-PCMA.sh b/tests/examples/rtp/server-alsasrc-PCMA.sh index c0fe0e7215..5340434ad7 100755 --- a/tests/examples/rtp/server-alsasrc-PCMA.sh +++ b/tests/examples/rtp/server-alsasrc-PCMA.sh @@ -1,22 +1,22 @@ #!/bin/sh # -# A simple RTP server +# A simple RTP server # sends the output of autoaudiosrc as alaw encoded RTP on port 5002, RTCP is sent on # port 5003. The destination is 127.0.0.1. # the receiver RTCP reports are received on port 5007 # -# .-------. .-------. .-------. .----------. .-------. -# |autoaudiosrc| |alawenc| |pcmapay| | rtpbin | |udpsink| RTP -# | src->sink src->sink src->send_rtp send_rtp->sink | port=5002 -# '-------' '-------' '-------' | | '-------' -# | | -# | | .-------. -# | | |udpsink| RTCP -# | send_rtcp->sink | port=5003 -# .-------. | | '-------' sync=false -# RTCP |udpsrc | | | async=false -# port=5007 | src->recv_rtcp | -# '-------' '----------' +# .--------. .-------. .-------. .----------. .-------. +# |audiosrc| |alawenc| |pcmapay| | rtpbin | |udpsink| RTP +# | src->sink src->sink src->send_rtp send_rtp->sink | port=5002 +# '--------' '-------' '-------' | | '-------' +# | | +# | | .-------. +# | | |udpsink| RTCP +# | send_rtcp->sink | port=5003 +# .-------. | | '-------' sync=false +# RTCP |udpsrc | | | async=false +# port=5007 | src->recv_rtcp | +# '-------' '----------' # change this to send the RTP data and RTCP to another host DEST=127.0.0.1 diff --git a/tests/examples/rtp/server-v4l2-H263p-alsasrc-AMR.sh b/tests/examples/rtp/server-v4l2-H263p-alsasrc-AMR.sh index 2a8dc5cf50..dd7fc8997a 100755 --- a/tests/examples/rtp/server-v4l2-H263p-alsasrc-AMR.sh +++ b/tests/examples/rtp/server-v4l2-H263p-alsasrc-AMR.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# A simple RTP server +# A simple RTP server # # change these to change the server sync. This causes the server to send the @@ -14,7 +14,7 @@ VCAPS="video/x-raw-yuv,width=352,height=288,framerate=15/1" DEST=192.168.1.126 gst-launch -v gstrtpbin name=rtpbin \ - v4l2src ! $VCAPS ! videorate ! ffmpegcolorspace ! ffenc_h263p ! rtph263ppay ! rtpbin.send_rtp_sink_0 \ + v4l2src ! videorate ! ffmpegcolorspace ! $VCAPS ! ffenc_h263p ! rtph263ppay ! rtpbin.send_rtp_sink_0 \ rtpbin.send_rtp_src_0 ! queue ! udpsink host=$HOST port=5000 ts-offset=$AOFFSET \ rtpbin.send_rtcp_src_0 ! udpsink host=$HOST port=5001 sync=false async=false \ udpsrc port=5005 ! rtpbin.recv_rtcp_sink_0 \ diff --git a/tests/examples/rtp/server-v4l2-H264-alsasrc-PCMA.sh b/tests/examples/rtp/server-v4l2-H264-alsasrc-PCMA.sh index 910b69e1a8..8dc4d70900 100755 --- a/tests/examples/rtp/server-v4l2-H264-alsasrc-PCMA.sh +++ b/tests/examples/rtp/server-v4l2-H264-alsasrc-PCMA.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# A simple RTP server +# A simple RTP server # sends the output of v4l2src as h264 encoded RTP on port 5000, RTCP is sent on # port 5001. The destination is 127.0.0.1. # the video receiver RTCP reports are received on port 5005 @@ -8,31 +8,31 @@ # port 5003. The destination is 127.0.0.1. # the receiver RTCP reports are received on port 5007 # -# .-------. .-------. .-------. .----------. .-------. -# |v4lssrc| |h264enc| |h264pay| | rtpbin | |udpsink| RTP -# | src->sink src->sink src->send_rtp send_rtp->sink | port=5000 -# '-------' '-------' '-------' | | '-------' -# | | -# | | .-------. -# | | |udpsink| RTCP -# | send_rtcp->sink | port=5001 -# .-------. | | '-------' sync=false -# RTCP |udpsrc | | | async=false -# port=5005 | src->recv_rtcp | -# '-------' | | -# | | -# .-------. .-------. .-------. | | .-------. -# |autoaudiosrc| |alawenc| |pcmapay| | rtpbin | |udpsink| RTP -# | src->sink src->sink src->send_rtp send_rtp->sink | port=5002 -# '-------' '-------' '-------' | | '-------' -# | | -# | | .-------. -# | | |udpsink| RTCP -# | send_rtcp->sink | port=5003 -# .-------. | | '-------' sync=false -# RTCP |udpsrc | | | async=false -# port=5007 | src->recv_rtcp | -# '-------' '----------' +# .-------. .-------. .-------. .----------. .-------. +# |v4lssrc| |h264enc| |h264pay| | rtpbin | |udpsink| RTP +# | src->sink src->sink src->send_rtp send_rtp->sink | port=5000 +# '-------' '-------' '-------' | | '-------' +# | | +# | | .-------. +# | | |udpsink| RTCP +# | send_rtcp->sink | port=5001 +# .-------. | | '-------' sync=false +# RTCP |udpsrc | | | async=false +# port=5005 | src->recv_rtcp | +# '-------' | | +# | | +# .--------. .-------. .-------. | | .-------. +# |audiosrc| |alawenc| |pcmapay| | rtpbin | |udpsink| RTP +# | src->sink src->sink src->send_rtp send_rtp->sink | port=5002 +# '--------' '-------' '-------' | | '-------' +# | | +# | | .-------. +# | | |udpsink| RTCP +# | send_rtcp->sink | port=5003 +# .-------. | | '-------' sync=false +# RTCP |udpsrc | | | async=false +# port=5007 | src->recv_rtcp | +# '-------' '----------' # # ideally we should transport the properties on the RTP udpsink pads to the # receiver in order to transmit the SPS and PPS earlier. @@ -41,7 +41,7 @@ DEST=127.0.0.1 # tuning parameters to make the sender send the streams out of sync. Can be used -# ot test the client RTCP synchronisation. +# ot test the client RTCP synchronisation. #VOFFSET=900000000 VOFFSET=0 AOFFSET=0 @@ -50,7 +50,7 @@ AOFFSET=0 VELEM="v4l2src" #VELEM="videotestsrc is-live=1" VCAPS="video/x-raw-yuv,width=352,height=288,framerate=15/1" -VSOURCE="$VELEM ! $VCAPS ! queue ! videorate ! ffmpegcolorspace" +VSOURCE="$VELEM ! queue ! videorate ! ffmpegcolorspace ! $VCAPS" VENC="x264enc tune=zerolatency byte-stream=true bitrate=300 ! rtph264pay" VRTPSINK="udpsink port=5000 host=$DEST ts-offset=$VOFFSET name=vrtpsink" diff --git a/tests/icles/.gitignore b/tests/icles/.gitignore index 9ba812160e..0b5704b32a 100644 --- a/tests/icles/.gitignore +++ b/tests/icles/.gitignore @@ -1,3 +1,4 @@ +equalizer-test gdkpixbufsink-test test-oss4 ximagesrc-test diff --git a/tests/icles/Makefile.am b/tests/icles/Makefile.am index 4405ecfb0f..5b1eb22b69 100644 --- a/tests/icles/Makefile.am +++ b/tests/icles/Makefile.am @@ -39,6 +39,10 @@ else X_TESTS = endif +equalizer_test_SOURCES = equalizer-test.c +equalizer_test_CFLAGS = $(GST_CFLAGS) +equalizer_test_LDADD = $(GST_LIBS) + videocrop_test_SOURCES = videocrop-test.c videocrop_test_CFLAGS = $(GST_CFLAGS) videocrop_test_LDADD = $(GST_LIBS) @@ -51,5 +55,5 @@ videocrop2_test_SOURCES = videocrop2-test.c videocrop2_test_CFLAGS = $(GST_CFLAGS) videocrop2_test_LDADD = $(GST_LIBS) -noinst_PROGRAMS = $(GTK_TESTS) $(OSS4_TESTS) $(V4L2_TESTS) $(X_TESTS) videocrop-test videobox-test videocrop2-test +noinst_PROGRAMS = $(GTK_TESTS) $(OSS4_TESTS) $(V4L2_TESTS) $(X_TESTS) equalizer-test videocrop-test videobox-test videocrop2-test diff --git a/tests/icles/equalizer-test.c b/tests/icles/equalizer-test.c new file mode 100644 index 0000000000..e8126fad3a --- /dev/null +++ b/tests/icles/equalizer-test.c @@ -0,0 +1,289 @@ +/* GStreamer test for the equalizer element + * Copyright (C) 2007 Tim-Philipp Müller + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Will tests the equalizer by fading all bands in and out one by one and + * finaly all together. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include +#include + +GST_DEBUG_CATEGORY_STATIC (equalizer_test_debug); +#define GST_CAT_DEFAULT equalizer_test_debug + +static GstBus *pipeline_bus; + +static gboolean +check_bus (GstClockTime max_wait_time) +{ + GstMessage *msg; + + msg = gst_bus_poll (pipeline_bus, GST_MESSAGE_ERROR | GST_MESSAGE_EOS, + max_wait_time); + + if (msg == NULL) + return FALSE; + + if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) { + GError *err = NULL; + gchar *debug = NULL; + + g_assert (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR); + gst_message_parse_error (msg, &err, &debug); + GST_ERROR ("ERROR: %s [%s]", err->message, debug); + g_print ("\n===========> ERROR: %s\n%s\n\n", err->message, debug); + g_error_free (err); + g_free (debug); + } + + if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS) { + g_print ("\n === EOS ===\n\n"); + } + + gst_message_unref (msg); + return TRUE; +} + +// fix below + +static void +equalizer_set_band_value (GstElement * eq, guint band, gdouble val) +{ + GstObject *child; + + child = gst_child_proxy_get_child_by_index (GST_CHILD_PROXY (eq), band); + g_object_set (child, "gain", val, NULL); + gst_object_unref (child); + g_print ("Band %2d: %.2f\n", band, val); +} + +static void +equalizer_set_all_band_values (GstElement * eq, guint num, gdouble val) +{ + gint i; + GstObject *child; + + for (i = 0; i < num; i++) { + child = gst_child_proxy_get_child_by_index (GST_CHILD_PROXY (eq), i); + g_object_set (child, "gain", val, NULL); + gst_object_unref (child); + } + g_print ("All bands: %.2f\n", val); +} + +// fix above + +static gboolean +equalizer_set_band_value_and_wait (GstElement * eq, guint band, gdouble val) +{ + equalizer_set_band_value (eq, band, val); + return check_bus (100 * GST_MSECOND); +} + +static gboolean +equalizer_set_all_band_values_and_wait (GstElement * eq, guint num, gdouble val) +{ + equalizer_set_all_band_values (eq, num, val); + return check_bus (100 * GST_MSECOND); +} + +static void +do_slider_fiddling (GstElement * playbin, GstElement * eq) +{ + gboolean stop; + guint num_bands, i; + gdouble d, step = 0.5; + + stop = FALSE; + + g_object_get (eq, "num-bands", &num_bands, NULL); + + g_print ("%u bands.\n", num_bands); + + while (!stop) { + for (i = 0; !stop && i < num_bands; ++i) { + d = -24.0; + while (!stop && d <= 12.0) { + stop = equalizer_set_band_value_and_wait (eq, i, d); + d += step; + } + d = 12.0; + while (!stop && d >= -24.0) { + stop = equalizer_set_band_value_and_wait (eq, i, d); + d -= step; + } + d = -24.0; + while (!stop && d <= 12.0) { + stop = equalizer_set_band_value_and_wait (eq, i, d); + d += step; + } + } + + d = 0.0; + while (!stop && d <= 12.0) { + stop = equalizer_set_all_band_values_and_wait (eq, num_bands, d); + d += step; + } + d = 12.0; + while (!stop && d >= -24.0) { + stop = equalizer_set_all_band_values_and_wait (eq, num_bands, d); + d -= step; + } + d = -24.0; + while (!stop && d <= 0.0) { + stop = equalizer_set_all_band_values_and_wait (eq, num_bands, d); + d += step; + } + } +} + +int +main (int argc, char **argv) +{ + gchar *opt_audiosink_str = NULL; + gchar **filenames = NULL; + const GOptionEntry test_goptions[] = { + {"audiosink", '\0', 0, G_OPTION_ARG_STRING, &opt_audiosink_str, + "audiosink to use (default: autoaudiosink)", NULL}, + {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL}, + {NULL, '\0', 0, 0, NULL, NULL, NULL} + }; + GOptionContext *ctx; + GError *opt_err = NULL; + + GstStateChangeReturn ret; + GstElement *playbin, *sink, *bin, *eq, *auconv; + GstPad *eq_sinkpad; + gchar *uri; + + if (!g_thread_supported ()) + g_thread_init (NULL); + + /* command line option parsing */ + ctx = g_option_context_new ("FILENAME"); + g_option_context_add_group (ctx, gst_init_get_option_group ()); + g_option_context_add_main_entries (ctx, test_goptions, NULL); + + if (!g_option_context_parse (ctx, &argc, &argv, &opt_err)) { + g_error ("Error parsing command line options: %s", opt_err->message); + return -1; + } + + GST_DEBUG_CATEGORY_INIT (equalizer_test_debug, "equalizertest", 0, "eqtest"); + + if (filenames == NULL || *filenames == NULL) { + g_printerr ("Please specify a file to play back\n"); + return -1; + } + + playbin = gst_element_factory_make ("playbin", "playbin"); + if (playbin == NULL) { + g_error ("Couldn't create 'playbin' element"); + return -1; + } + + if (opt_audiosink_str) { + g_print ("Trying audiosink '%s' ...", opt_audiosink_str); + sink = gst_element_factory_make (opt_audiosink_str, "sink"); + g_print ("%s\n", (sink) ? "ok" : "element couldn't be created"); + } else { + sink = NULL; + } + if (sink == NULL) { + g_print ("Trying audiosink '%s' ...", "autoaudiosink"); + sink = gst_element_factory_make ("autoaudiosink", "sink"); + g_print ("%s\n", (sink) ? "ok" : "element couldn't be created"); + } + if (sink == NULL) { + g_print ("Trying audiosink '%s' ...", "alsasink"); + sink = gst_element_factory_make ("alsasink", "sink"); + g_print ("%s\n", (sink) ? "ok" : "element couldn't be created"); + } + if (sink == NULL) { + g_print ("Trying audiosink '%s' ...", "osssink"); + sink = gst_element_factory_make ("osssink", "sink"); + g_print ("%s\n", (sink) ? "ok" : "element couldn't be created"); + } + + g_assert (sink != NULL); + + bin = gst_bin_new ("ausinkbin"); + g_assert (bin != NULL); + + eq = gst_element_factory_make ("equalizer-nbands", "equalizer"); + g_assert (eq != NULL); + + auconv = gst_element_factory_make ("audioconvert", "eqauconv"); + g_assert (auconv != NULL); + + gst_bin_add_many (GST_BIN (bin), eq, auconv, sink, NULL); + + if (!gst_element_link (eq, auconv)) + g_error ("Failed to link equalizer to audioconvert"); + + if (!gst_element_link (auconv, sink)) + g_error ("Failed to link audioconvert to audio sink"); + + eq_sinkpad = gst_element_get_static_pad (eq, "sink"); + g_assert (eq_sinkpad != NULL); + + gst_element_add_pad (bin, gst_ghost_pad_new (NULL, eq_sinkpad)); + gst_object_unref (eq_sinkpad); + + g_object_set (playbin, "audio-sink", bin, NULL); + + /* won't work: uri = gst_uri_construct ("file", filenames[0]); */ + uri = g_strdup_printf ("file://%s", filenames[0]); + g_object_set (playbin, "uri", uri, NULL); + g_free (uri); + + pipeline_bus = GST_ELEMENT_BUS (playbin); + + ret = gst_element_set_state (playbin, GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) { + g_printerr ("Failed to set playbin to PLAYING\n"); + check_bus (1 * GST_SECOND); + return -1; + } + + ret = gst_element_get_state (playbin, NULL, NULL, 5 * GST_SECOND); + if (ret == GST_STATE_CHANGE_ASYNC) { + g_printerr ("Failed to go to PLAYING in 5 seconds, bailing out\n"); + return -1; + } else if (ret != GST_STATE_CHANGE_SUCCESS) { + g_printerr ("State change to PLAYING failed\n"); + check_bus (1 * GST_SECOND); + return -1; + } + + g_print ("Playing ...\n"); + do_slider_fiddling (playbin, eq); + + gst_element_set_state (playbin, GST_STATE_NULL); + gst_object_unref (playbin); + + return 0; +} diff --git a/tools/.gitignore b/tools/.gitignore deleted file mode 100644 index e3970bba92..0000000000 --- a/tools/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -gst-launch-ext-?.? -gst-visualise-?.? -gst-launch-ext-?.?.1 -gst-visualise-?.?.1 diff --git a/tools/Makefile.am b/tools/Makefile.am deleted file mode 100644 index 1b8ab850ad..0000000000 --- a/tools/Makefile.am +++ /dev/null @@ -1,39 +0,0 @@ -bin_SCRIPTS = \ - gst-launch-ext-@GST_MAJORMINOR@ \ - gst-visualise-@GST_MAJORMINOR@ - -man_MANS = \ - gst-launch-ext-@GST_MAJORMINOR@.1 \ - gst-visualise-@GST_MAJORMINOR@.1 - -CLEANFILES = $(man_MANS) $(bin_SCRIPTS) - -EXTRA_DIST = \ - gst-launch-ext-m.m gst-visualise-m.m \ - gst-launch-ext.1.in gst-visualise.1.in - -# generate versioned scripts from templates -%-@GST_MAJORMINOR@: %-m.m - sed -e s,\@GST_MAJORMINOR\@,@GST_MAJORMINOR@,g $< > $@ - chmod +x $@ - -# generate man pages -%-@GST_MAJORMINOR@.1: %.1.in - sed \ - -e s,gst-complete,gst-complete-@GST_MAJORMINOR@,g \ - -e s,gst-compprep,gst-compprep-@GST_MAJORMINOR@,g \ - -e s,gst-feedback,gst-feedback-@GST_MAJORMINOR@,g \ - -e s,gst-inspect,gst-inspect-@GST_MAJORMINOR@,g \ - -e s,gst-launch,gst-launch-@GST_MAJORMINOR@,g \ - -e s,gst-launch-ext,gst-launch-ext-@GST_MAJORMINOR@,g \ - -e s,gst-md5sum,gst-md5sum-@GST_MAJORMINOR@,g \ - -e s,gst-register,gst-register-@GST_MAJORMINOR@,g \ - -e s,gst-typefind,gst-typefind-@GST_MAJORMINOR@,g \ - -e s,gst-visualise,gst-visualise-@GST_MAJORMINOR@,g \ - -e s,gst-xmllaunch,gst-xmllaunch-@GST_MAJORMINOR@,g \ - $< >$@ - -all: all-am chmod - -chmod: $(bin_SCRIPTS) - chmod +x $^ diff --git a/tools/README.filterstamp b/tools/README.filterstamp deleted file mode 100644 index a7b3d21e6f..0000000000 --- a/tools/README.filterstamp +++ /dev/null @@ -1,24 +0,0 @@ -filterstamp.sh is a script to copy a filter and change filenames and all -occurrences of the old name to the new name. - -This is used for writing new audio filters. The best one to copy for now is -passthrough; it works on raw/audio int or float data. - -If your new filter is called StereoPan, for example, then do this : - -cd gst -../tools/filterstamp.sh Passthrough StereoPan -cd stereopan -make - -(Please note the upper- and lower-case !) - -You should also add a line to configure.ac to make sure the Makefiles are built -correctly. Just search for "passthrough" and add corresponding "stereopan" (or -whatever your plugin is) lines. - -Register it, then try it out. It shouldn't do anything ! - -Now edit the filter.func in the new directory; this file contains the body -of the main processing loop. - diff --git a/tools/filterstamp.sh b/tools/filterstamp.sh deleted file mode 100755 index 56b3d8d9ab..0000000000 --- a/tools/filterstamp.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash - -# copies from gstreamer filter boilerplate -# to new filter -# changing file names and function references - -# thomas@apestaart.org - -# modified 23 aug 2001 apwingo@eos.ncsu.edu: -# conform better to gtk naming conventions (GstFoo->gst_foo in functions, etc) - -if [ "$1" = "" ] -then - echo "please specify the filter to copy FROM (e.g. Passthrough)" - exit -fi - -if [ "$2" = "" ] -then - echo "please specify the filter to copy TO (e.g. NewFilter)" - exit -fi - -FROM=$1 -TO=$2 -FROM_LC=`echo $FROM | tr [A-Z] [a-z]` -TO_LC=`echo $TO | tr [A-Z] [a-z]` -FROM_UC=`echo $FROM | tr [a-z] [A-Z]` -TO_UC=`echo $TO | tr [a-z] [A-Z]` -FROM_LC_UNDERSCORE=`echo $FROM | perl -n -p -e 's/([a-z])([A-Z])/$1_$2/g; tr/A-Z/a-z/'` -TO_LC_UNDERSCORE=`echo $TO | perl -n -p -e 's/([a-z])([A-Z])/$1_$2/g; tr/A-Z/a-z/'` - -echo "Copying filter boilerplate $FROM to new filter $TO..." - -if [ ! -d $FROM_LC ] -then - echo "Filter directory $FROM_LC does not exist !" - exit -fi - -if [ -d $TO_LC ] -then - echo "Filter directory $TO_LC already exists !" - exit -fi - -cp -r $FROM_LC $TO_LC - -cd $TO_LC - -for a in *$FROM_LC*; do mv $a `echo $a | sed s/$FROM_LC/$TO_LC/g`; done - -perl -i -p -e "s/$FROM/$TO/g" * -perl -i -p -e "s/${FROM_LC_UNDERSCORE}_/${TO_LC_UNDERSCORE}_/g" * -perl -i -p -e "s/$FROM_LC/$TO_LC/g" * -perl -i -p -e "s/$FROM_UC/$TO_UC/g" * - diff --git a/tools/gst-launch-ext-m.m b/tools/gst-launch-ext-m.m deleted file mode 100644 index 915f2dfa86..0000000000 --- a/tools/gst-launch-ext-m.m +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/perl -w -use strict; - -# launch a gst-launch pipeline for the supplied media file -# use the extension to determine the gst-launch pipeline -# make use of default output sinks - -my (%pipes, %cfg); - -sub extension -{ - my $path = shift; - my $ext; - - # get only the bit after the last period. We don't deal with - # .tar.gz extensions do we ? - if ($path =~ /\./) - { - $ext = $path; - $ext =~ s/^.*\.//; - } - else { $ext = ""; } - - return $ext; -} - -sub read_config -{ - my $command = shift; - - my $config_file = $ENV{HOME}."/.gst"; - if (-e $config_file) - { - open CONFIG, $config_file; - while () - { - chomp; - s/#.*//; - s/\s+$//; - next unless length; - my ($var, $value) = split (/\s*=\s*/, $_, 2); - $cfg{$var} = $value; - } - if (!($cfg{AUDIOSINK})) - { - print "Please add an AUDIOSINK to $config_file !\n"; - } - if (!($cfg{VIDEOSINK})) - { - print "Please add a VIDEOSINK to $config_file !\n"; - } - } - else - { - print "No configuration file $config_file found. You might want to create one.\n"; - print "This is not an error, just a friendly reminder... Check the man page.\n\n"; - } - if (!defined $cfg{AUDIOSINK}) { $cfg{AUDIOSINK} = "osssink"; } - if (!defined $cfg{VIDEOSINK}) { $cfg{VIDEOSINK} = "ffmpegcolorspace ! xvimagesink"; } - if (!defined $cfg{CVS_PATH}) { $cfg{CVS_PATH} = $ENV{HOME}."/gst/cvs"; } - - if ($command =~ /(.+)\/gst-launch-ext-@GST_MAJORMINOR@$/) - { $cfg{COMMAND_PATH} = "$1"; } - else - { $cfg{COMMAND_PATH} = ""; } -} - -sub playfile($$) -{ - my ($file, $ext) = @_; - my $command; - my $pipe; - my $path = "\$PATH:".$cfg{CVS_PATH}."/gstreamer/tools"; - - if ($cfg{COMMAND_PATH} ne "") { - $path = $cfg{COMMAND_PATH}.":$path"; - } - - $ext = lc $ext; - - if ($cfg{VISUALIZER} && ($pipe = $pipes{"vis." . $ext})) - { - $command = "gst-launch-@GST_MAJORMINOR@ filesrc location=\"$file\" ! $pipe"; - print "Running command-line\n$command\n\n"; - system ("PATH=$path $command"); - } - elsif ($pipe = $pipes{$ext}) - { - $command = "gst-launch-@GST_MAJORMINOR@ filesrc location=\"$file\" ! $pipe"; - print "Running command-line\n$command\n\n"; - system ("PATH=$path $command"); - } - else - { - print "No suitable pipe found for extension $ext.\n"; - } -} - -### main - -read_config ($0); - -%pipes = ( - "ac3", "a52dec ! $cfg{AUDIOSINK}", - "au", "auparse ! $cfg{AUDIOSINK}", - "avi", "decodebin name=d { d. ! queue ! $cfg{VIDEOSINK} } { d. ! queue ! audioconvert ! audioscale ! $cfg{AUDIOSINK} }", - "asf", "decodebin name=d ! { d. ! queue ! $cfg{VIDEOSINK} } { d. ! queue ! audioconvert ! audioscale ! $cfg{AUDIOSINK} }", - "flac", "flacdec ! $cfg{AUDIOSINK}", - "fli", "flxdec ! colorspace ! $cfg{VIDEOSINK}", - "m1v", "mpeg2dec ! $cfg{VIDEOSINK}", - "m2v", "mpeg2dec ! $cfg{VIDEOSINK}", - "m4a", "qtdemux .audio_00 ! { queue ! faad ! $cfg{AUDIOSINK} }", - "aac", "faad ! $cfg{AUDIOSINK}", - "mod", "modplug ! $cfg{AUDIOSINK}", - "mpc", "musepackdec ! $cfg{AUDIOSINK}", - "mp2", "mad ! $cfg{AUDIOSINK}", - "mp3", "mad ! $cfg{AUDIOSINK}", - "mp4", "decodebin name=d { d. ! queue ! ffmpegcolorspace ! videoscale ! $cfg{VIDEOSINK} } { d. ! queue ! audioconvert ! audioscale ! $cfg{AUDIOSINK} }", - "mpeg", "mpegdemux name=d { d.audio_00 ! queue ! mad ! audioconvert ! audioscale ! $cfg{AUDIOSINK} } { d.video_00 ! queue ! mpeg2dec ! $cfg{VIDEOSINK} }", - "mpg", "mpegdemux name=demux { demux.video_00 ! queue ! mpeg2dec ! ffmpegcolorspace ! $cfg{VIDEOSINK} } { demux.audio_00 ! queue ! mad ! $cfg{AUDIOSINK} }", - "ogg", "oggdemux ! vorbisdec ! audioconvert ! $cfg{AUDIOSINK}", - "sid", "siddec ! $cfg{AUDIOSINK}", - "swf", "swfdec name=swfdec ! { queue ! colorspace ! $cfg{VIDEOSINK} } { swfdec. ! queue ! $cfg{AUDIOSINK} }", - "vob", "mpegdemux name=d { d.video_00 ! queue ! mpeg2dec ! $cfg{VIDEOSINK} } { d.audio_00 ! queue ! a52dec ! audioconvert ! audioscale ! $cfg{AUDIOSINK} }", - "wav", "wavparse ! $cfg{AUDIOSINK}", - "wm", "asfdemux name=demux ! { queue ! spider ! $cfg{VIDEOSINK} } { demux. ! queue ! spider ! $cfg{AUDIOSINK} }", -### a wma file can use wmav1 or wmav2 codec so we must use spider to decode it - "wma", "asfdemux name=demux ! spider ! $cfg{AUDIOSINK}", - "wmv", "asfdemux name=demux ! { queue ! spider ! $cfg{VIDEOSINK} } { demux. ! queue ! spider ! $cfg{AUDIOSINK} }", - "mkv", "matroskademux name=demux ! { queue ! spider ! $cfg{VIDEOSINK} } { demux. ! queue ! spider ! $cfg{AUDIOSINK} }", - "mka", "matroskademux ! spider ! $cfg{AUDIOSINK}", - "mov", "decodebin name=d { d. ! queue ! ffmpegcolorspace ! videoscale ! $cfg{VIDEOSINK} } { d. ! queue ! audioconvert ! audioscale ! $cfg{AUDIOSINK} }", -); - -if ($cfg{VISUALIZER}) { - %pipes = ( - %pipes, - "vis.mp3", "mad ! tee name=tee silent=true ! queue leaky=1 ! { $cfg{VISUALIZER} ! ffmpegcolorspace ! $cfg{VIDEOSINK} } tee. ! $cfg{AUDIOSINK}", - "vis.ogg", "vorbisdec ! tee name=tee silent=true ! queue leaky=1 ! { $cfg{VISUALIZER} ! ffmpegcolorspace ! $cfg{VIDEOSINK} } tee. ! $cfg{AUDIOSINK}", - "vis.wav", "wavparse ! tee name=tee silent=true ! queue leaky=1 ! { $cfg{VISUALIZER} ! ffmpegcolorspace ! $cfg{VIDEOSINK} } tee. ! $cfg{AUDIOSINK}", - ); -} - -if ($#ARGV == -1) { - print STDERR "Usage: gst-launch-ext-@GST_MAJORMINOR@ filename[s]\n"; - exit 1; -} - -my $file; -while ($file = shift @ARGV) { - my $ext = extension ($file); - if (!$ext) { - print "file $file doesn't have an extension !\n"; - exit; - } - if ($ext eq 'm3u') - { - open (PLAYLIST, '<', $file); - my $file2; - while ($file2 = ) { - chomp $file2; - my $ext2 = extension ($file2); - playfile($file2, $ext2); - } - close PLAYLIST; - } else { - playfile($file, $ext); - } -} diff --git a/tools/gst-launch-ext.1.in b/tools/gst-launch-ext.1.in deleted file mode 100644 index fd2ebf1be3..0000000000 --- a/tools/gst-launch-ext.1.in +++ /dev/null @@ -1,45 +0,0 @@ -.TH "GStreamer" "1" "February 2002" "" "" -.SH "NAME" -gst\-launch\-ext \- Run a predefined GStreamer pipeline -.SH "SYNOPSIS" -\fBgst\-launch\-ext\fR \fIfilename [filename...]\fR -.SH "DESCRIPTION" -.LP -\fIgst\-launch\-ext\fP is a tool that is used to run a basic predefined -\fIGStreamer\fP pipeline. This application is only used as a quick test to -ensure proper working of codecs and GStreamer. It doesn't handle more advanced -features like synchronisation. - -All supported formats in GStreamer should be playable by simply typing: - - gst\-launch\-ext filename - -It will also print out the pipeline it uses, so you can customize it using -cut and paste. - -.SH "CONFIGURATION" -.LP -\fIgst\-launch\-ext\fP can be configured by creating a .gst file in your -home directory. This is a perl-style configuration file and can override -the defaults for audio and video output sinks. - -Here is an example .gst file that implements the same defaults as hard-coded -in the script : - -AUDIOSINK = osssink -VIDEOSINK = ffmpegcolorspace ! xvimagesink - -You can change osssink to esdsink or alsasink (if you have -the plug-in), and you can change xvimagesink to ximagesink, aasink -or sdlvideosink. - -Other plug-ins might be used as well if GStreamer has them. - -.SH "SEE ALSO" -.BR gst\-guilaunch (1), -.BR gst\-complete (1), -.BR gst\-register (1), -.BR gst\-inspect (1), -.BR gst\-launch (1), -.SH "AUTHOR" -The GStreamer team at http://gstreamer.net/ diff --git a/tools/gst-visualise-m.m b/tools/gst-visualise-m.m deleted file mode 100644 index 36d4497413..0000000000 --- a/tools/gst-visualise-m.m +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/perl -w - -# launch a gst-launch pipeline to display a visualisation of the -# input audio. -# make use of default input srcs. -# visualisation plugin is specified on command line. - -### packages - -use File::Basename; - - -my (%pipes, %cfg); - -sub read_config -{ - my $config_file = `echo -n ~`."/.gst"; - if (-e $config_file) - { - open CONFIG, $config_file; - while () - { - chomp; - s/#.*//; - s/\s+$//; - next unless length; - my ($var, $value) = split (/\s*=\s*/, $_, 2); - $cfg{$var} = $value; - } - if (!($cfg{AUDIOSRC})) - { - print "Please add an AUDIOSRC to $config_file !\n"; - } - if (!($cfg{VIDEOSINK})) - { - print "Please add a VIDEOSINK to $config_file !\n"; - } - } - else - { - print "No configuration file $config_file found. You might want to create one.\n"; - } - if (!defined $cfg{AUDIOSRC}) { $cfg{AUDIOSRC} = "osssrc"; } - if (!defined $cfg{VIDEOSINK}) { $cfg{VIDEOSINK} = "xvimagesink"; } - if (!defined $cfg{CVS_PATH}) { $cfg{CVS_PATH} = `echo -n ~`."/gst/cvs"; } -} - -sub visualise(@) -{ - my $vis = $cfg{VISUALIZER}; - $vis = shift() if ($#_ != -1); - $vis = "goom" unless $vis; - - my $pipe; - $pipe = $vis unless $pipe = $pipes{$vis}; - - $command = "gst-launch-@GST_MAJORMINOR@ $cfg{AUDIOSRC} ! $pipe ! { queue ! ffmpegcolorspace ! $cfg{VIDEOSINK} }"; - print "Running $command\n"; - system ("PATH=\$PATH:".$cfg{CVS_PATH}."/gstreamer/tools $command"); -} - -### main - -read_config (); - -%pipes = ( - "goom", "goom", - "chart", "audioconvert ! chart", - "synaesthesia", "synaesthesia", - "monoscope", "audioconvert ! monoscope" -); - -if ($#ARGV > 0) { - print STDERR "Usage: gst-visualise [visualiser]\n"; - exit 1; -} - -visualise(@ARGV); - diff --git a/tools/gst-visualise.1.in b/tools/gst-visualise.1.in deleted file mode 100644 index f61446ece9..0000000000 --- a/tools/gst-visualise.1.in +++ /dev/null @@ -1,35 +0,0 @@ -.TH "GStreamer" "1" "February 2002" "" "" -.SH "NAME" -gst\-visualise \- Run a GStreamer pipeline to display an audio visualisation -.SH "SYNOPSIS" -\fBgst\-visualise\fR \fI[visualiser]\fR -.SH "DESCRIPTION" -.LP -\fIgst\-visualise\fP is a tool that is used to run a basic \fIGStreamer\fP pipeline, to display a graphical visualisation of an audio stream. - -By default, the audio stream is read from ESD (the Enlightened Sound Daemon), -but this can be changed by setting the AUDIOSRC parameter in ~/.gst. For -example, you might set "AUDIOSRC=osssrc" to display a visualisation of the -sound input to your soundcard. - -You can select a visualiser by providing a parameter naming the visualiser. -For example: - - gst\-visualise synaesthesia - -will use the synaesthesia plugin. If no visualiser is named, the VISUALIZER -property in ~/.gst will be used. If this is not specified either, the goom -visualiser will be used. - -The videosink to use to display the visualisation will be read from the -VIDEOSINK parameter in ~/.gst, defaulting to sdlvideosink. - -.SH "SEE ALSO" -.BR gst\-launch\-ext (1), -.BR gst\-guilaunch (1), -.BR gst\-complete (1), -.BR gst\-register (1), -.BR gst\-inspect (1), -.BR gst\-launch (1), -.SH "AUTHOR" -The GStreamer team at http://gstreamer.net/ diff --git a/win32/common/config.h b/win32/common/config.h index 521659e1d1..8abfbb58dd 100644 --- a/win32/common/config.h +++ b/win32/common/config.h @@ -9,7 +9,7 @@ #undef AC_APPLE_UNIVERSAL_BUILD /* Default audio sink */ -#define DEFAULT_AUDIOSINK "directaudiosink" +#define DEFAULT_AUDIOSINK "directsoundsink" /* Default audio source */ #define DEFAULT_AUDIOSRC "audiotestsrc" @@ -49,7 +49,7 @@ #define GST_PACKAGE_ORIGIN "Unknown package origin" /* GStreamer package release date/time for plugins as YYYY-MM-DD */ -#define GST_PACKAGE_RELEASE_DATETIME "2010-12-02T18:08Z" +#define GST_PACKAGE_RELEASE_DATETIME "2011-01-27T15:30Z" /* struct v4l2_buffer missing */ #undef GST_V4L2_MISSING_BUFDECL @@ -204,6 +204,9 @@ /* Define to 1 if you have the `isinf' function. */ #undef HAVE_ISINF +/* Define to enable Jack (used by jack). */ +#undef HAVE_JACK + /* Define to enable jpeg library (used by jpeg). */ #undef HAVE_JPEG @@ -383,7 +386,7 @@ #define PACKAGE_NAME "GStreamer Good Plug-ins" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "GStreamer Good Plug-ins 0.10.26.1" +#define PACKAGE_STRING "GStreamer Good Plug-ins 0.10.27.1" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "gst-plugins-good" @@ -392,7 +395,7 @@ #undef PACKAGE_URL /* Define to the version of this package. */ -#define PACKAGE_VERSION "0.10.26.1" +#define PACKAGE_VERSION "0.10.27.1" /* directory where plugins are located */ #ifdef _DEBUG @@ -423,7 +426,7 @@ #undef STDC_HEADERS /* Version number of package */ -#define VERSION "0.10.26.1" +#define VERSION "0.10.27.1" /* old wavpack API */ #undef WAVPACK_OLD_API