-Subproject commit 46dfcea233cf6df83e3771d8a8066e87d614f893
+Subproject commit 69b981f10caa234ad0ff639179d0fda8505bd94b
translit(dnm, m, l) AM_CONDITIONAL(USE_LIBCACA, true)
AG_GST_CHECK_FEATURE(LIBCACA, [libcaca coloured ASCII art], cacasink, [
AG_GST_PKG_CHECK_MODULES(LIBCACA, caca)
- dnl only newer versions of libcaca ship caca.pc, so try caca-config as well
- if test "x$HAVE_LIBCACA" != "xyes"; then
- AG_GST_CHECK_CONFIGPROG(LIBCACA, caca-config)
- dnl see if it compilation works too, might not if we are cross-compiling
- if test "x$HAVE_LIBCACA" = "xyes"; then
- AC_CHECK_LIB([caca], [caca_init], [HAVE_LIBCACA=yes],
- [HAVE_LIBCACA=no], [$LIBCACA_LIBS])
- fi
- fi
])
dnl *** libdv ***
<ARG>
<NAME>GstUDPSrc::sockfd</NAME>
<TYPE>gint</TYPE>
-<RANGE>>= G_MAXULONG</RANGE>
+<RANGE>>= -1</RANGE>
<FLAGS>rw</FLAGS>
<NICK>Socket Handle</NICK>
<BLURB>Socket to use for UDP reception. (-1 == allocate).</BLURB>
<ARG>
<NAME>GstUDPSrc::sock</NAME>
<TYPE>gint</TYPE>
-<RANGE>>= G_MAXULONG</RANGE>
+<RANGE>>= -1</RANGE>
<FLAGS>r</FLAGS>
<NICK>Socket Handle</NICK>
<BLURB>Socket currently in use for UDP reception. (-1 = no socket).</BLURB>
<ARG>
<NAME>GstDV1394Src::port</NAME>
<TYPE>gint</TYPE>
-<RANGE>[G_MAXULONG,16]</RANGE>
+<RANGE>[-1,16]</RANGE>
<FLAGS>rw</FLAGS>
<NICK>Port</NICK>
<BLURB>Port number (-1 automatic).</BLURB>
<ARG>
<NAME>GstTest::allowed-timestamp-deviation</NAME>
<TYPE>gint64</TYPE>
-<RANGE>>= G_MAXULONG</RANGE>
+<RANGE>>= -1</RANGE>
<FLAGS>rwx</FLAGS>
<NICK>allowed timestamp deviation</NICK>
<BLURB>allowed average difference in usec between timestamp of next buffer and expected timestamp from analyzing last buffer.</BLURB>
<ARG>
<NAME>GstTest::buffer-count</NAME>
<TYPE>gint64</TYPE>
-<RANGE>>= G_MAXULONG</RANGE>
+<RANGE>>= -1</RANGE>
<FLAGS>r</FLAGS>
<NICK>buffer count</NICK>
<BLURB>number of buffers in stream.</BLURB>
<ARG>
<NAME>GstTest::expected-buffer-count</NAME>
<TYPE>gint64</TYPE>
-<RANGE>>= G_MAXULONG</RANGE>
+<RANGE>>= -1</RANGE>
<FLAGS>rwx</FLAGS>
<NICK>expected buffer count</NICK>
<BLURB>expected number of buffers in stream.</BLURB>
<ARG>
<NAME>GstTest::expected-length</NAME>
<TYPE>gint64</TYPE>
-<RANGE>>= G_MAXULONG</RANGE>
+<RANGE>>= -1</RANGE>
<FLAGS>rwx</FLAGS>
<NICK>expected length</NICK>
<BLURB>expected length of stream.</BLURB>
<ARG>
<NAME>GstTest::length</NAME>
<TYPE>gint64</TYPE>
-<RANGE>>= G_MAXULONG</RANGE>
+<RANGE>>= -1</RANGE>
<FLAGS>r</FLAGS>
<NICK>length</NICK>
<BLURB>length of stream.</BLURB>
<ARG>
<NAME>GstTest::timestamp-deviation</NAME>
<TYPE>gint64</TYPE>
-<RANGE>>= G_MAXULONG</RANGE>
+<RANGE>>= -1</RANGE>
<FLAGS>r</FLAGS>
<NICK>timestamp deviation</NICK>
<BLURB>average difference in usec between timestamp of next buffer and expected timestamp from analyzing last buffer.</BLURB>
<ARG>
<NAME>GstBreakMyData::set-to</NAME>
<TYPE>gint</TYPE>
-<RANGE>[G_MAXULONG,255]</RANGE>
+<RANGE>[-1,255]</RANGE>
<FLAGS>rwx</FLAGS>
<NICK>set-to</NICK>
<BLURB>set changed bytes to this value (-1 means random value.</BLURB>
<ARG>
<NAME>GstDynUDPSink::sockfd</NAME>
<TYPE>gint</TYPE>
-<RANGE>[G_MAXULONG,32767]</RANGE>
+<RANGE>[-1,32767]</RANGE>
<FLAGS>rw</FLAGS>
<NICK>socket handle</NICK>
<BLURB>Socket to use for UDP sending. (-1 == allocate).</BLURB>
<ARG>
<NAME>GstMultiUDPSink::sock</NAME>
<TYPE>gint</TYPE>
-<RANGE>>= G_MAXULONG</RANGE>
+<RANGE>>= -1</RANGE>
<FLAGS>r</FLAGS>
<NICK>Socket Handle</NICK>
<BLURB>Socket currently in use for UDP sending. (-1 == no socket).</BLURB>
<ARG>
<NAME>GstMultiUDPSink::sockfd</NAME>
<TYPE>gint</TYPE>
-<RANGE>>= G_MAXULONG</RANGE>
+<RANGE>>= -1</RANGE>
<FLAGS>rw</FLAGS>
<NICK>Socket Handle</NICK>
<BLURB>Socket to use for UDP sending. (-1 == allocate).</BLURB>
<ARG>
<NAME>GstMultiUDPSink::qos-dscp</NAME>
<TYPE>gint</TYPE>
-<RANGE>[G_MAXULONG,63]</RANGE>
+<RANGE>[-1,63]</RANGE>
<FLAGS>rw</FLAGS>
<NICK>QoS diff srv code point</NICK>
<BLURB>Quality of Service, differentiated services code point (-1 default).</BLURB>
<ARG>
<NAME>GstXImageSrc::screen-num</NAME>
<TYPE>guint</TYPE>
-<RANGE><= G_MAXINT</RANGE>
+<RANGE><= G_MAXLONG</RANGE>
<FLAGS>rw</FLAGS>
<NICK>Screen number</NICK>
<BLURB>X Screen Number.</BLURB>
<ARG>
<NAME>GstXImageSrc::endx</NAME>
<TYPE>guint</TYPE>
-<RANGE><= G_MAXINT</RANGE>
+<RANGE><= G_MAXLONG</RANGE>
<FLAGS>rw</FLAGS>
<NICK>End X</NICK>
<BLURB>X coordinate of bottom right corner of area to be recorded (0 for bottom right of screen).</BLURB>
<ARG>
<NAME>GstXImageSrc::endy</NAME>
<TYPE>guint</TYPE>
-<RANGE><= G_MAXINT</RANGE>
+<RANGE><= G_MAXLONG</RANGE>
<FLAGS>rw</FLAGS>
<NICK>End Y</NICK>
<BLURB>Y coordinate of bottom right corner of area to be recorded (0 for bottom right of screen).</BLURB>
<ARG>
<NAME>GstXImageSrc::startx</NAME>
<TYPE>guint</TYPE>
-<RANGE><= G_MAXINT</RANGE>
+<RANGE><= G_MAXLONG</RANGE>
<FLAGS>rw</FLAGS>
<NICK>Start X co-ordinate</NICK>
<BLURB>X coordinate of top left corner of area to be recorded (0 for top left of screen).</BLURB>
<ARG>
<NAME>GstXImageSrc::starty</NAME>
<TYPE>guint</TYPE>
-<RANGE><= G_MAXINT</RANGE>
+<RANGE><= G_MAXLONG</RANGE>
<FLAGS>rw</FLAGS>
<NICK>Start Y co-ordinate</NICK>
<BLURB>Y coordinate of top left corner of area to be recorded (0 for top left of screen).</BLURB>
<ARG>
<NAME>GstJpegDec::max-errors</NAME>
<TYPE>gint</TYPE>
-<RANGE>>= G_MAXULONG</RANGE>
+<RANGE>>= -1</RANGE>
<FLAGS>rw</FLAGS>
<NICK>Maximum Consecutive Decoding Errors</NICK>
<BLURB>Error out after receiving N consecutive decoding errors (-1 = never fail, 0 = automatic, 1 = fail on first error).</BLURB>
<ARG>
<NAME>GstV4l2Src::device-fd</NAME>
<TYPE>gint</TYPE>
-<RANGE>>= G_MAXULONG</RANGE>
+<RANGE>>= -1</RANGE>
<FLAGS>r</FLAGS>
<NICK>File descriptor</NICK>
<BLURB>File descriptor of the device.</BLURB>
<ARG>
<NAME>GstRndBufferSize::max</NAME>
<TYPE>glong</TYPE>
-<RANGE>[1,G_MAXINT]</RANGE>
+<RANGE>>= 1</RANGE>
<FLAGS>rwx</FLAGS>
<NICK>maximum</NICK>
<BLURB>maximum buffer size.</BLURB>
<ARG>
<NAME>GstRndBufferSize::min</NAME>
<TYPE>glong</TYPE>
-<RANGE>[0,G_MAXINT]</RANGE>
+<RANGE>>= 0</RANGE>
<FLAGS>rwx</FLAGS>
<NICK>mininum</NICK>
<BLURB>mininum buffer size.</BLURB>
<ARG>
<NAME>GstRndBufferSize::seed</NAME>
<TYPE>gulong</TYPE>
-<RANGE><= G_MAXUINT</RANGE>
+<RANGE></RANGE>
<FLAGS>rwx</FLAGS>
<NICK>random number seed</NICK>
<BLURB>seed for randomness (initialized when going from READY to PAUSED).</BLURB>
<ARG>
<NAME>GstHDV1394Src::port</NAME>
<TYPE>gint</TYPE>
-<RANGE>[G_MAXULONG,16]</RANGE>
+<RANGE>[-1,16]</RANGE>
<FLAGS>rw</FLAGS>
<NICK>Port</NICK>
<BLURB>Port number (-1 automatic).</BLURB>
</ARG>
<ARG>
+<NAME>GstDeinterlace::drop-orphans</NAME>
+<TYPE>gboolean</TYPE>
+<RANGE></RANGE>
+<FLAGS>rw</FLAGS>
+<NICK>drop-orphans</NICK>
+<BLURB>Drop orphan fields at the beginning of telecine patterns in active locking mode.</BLURB>
+<DEFAULT>TRUE</DEFAULT>
+</ARG>
+
+<ARG>
+<NAME>GstDeinterlace::ignore-obscure</NAME>
+<TYPE>gboolean</TYPE>
+<RANGE></RANGE>
+<FLAGS>rw</FLAGS>
+<NICK>ignore-obscure</NICK>
+<BLURB>Ignore obscure telecine patterns (only consider P, I and 2:3 variants).</BLURB>
+<DEFAULT>TRUE</DEFAULT>
+</ARG>
+
+<ARG>
+<NAME>GstDeinterlace::locking</NAME>
+<TYPE>GstDeinterlaceLocking</TYPE>
+<RANGE></RANGE>
+<FLAGS>rw</FLAGS>
+<NICK>locking</NICK>
+<BLURB>Pattern locking mode.</BLURB>
+<DEFAULT>No pattern locking</DEFAULT>
+</ARG>
+
+<ARG>
<NAME>GstAgingTV::color-aging</NAME>
<TYPE>gboolean</TYPE>
<RANGE></RANGE>
<ARG>
<NAME>GstOpTV::threshold</NAME>
<TYPE>guint</TYPE>
-<RANGE><= G_MAXINT</RANGE>
+<RANGE><= G_MAXLONG</RANGE>
<FLAGS>rw</FLAGS>
<NICK>Threshold</NICK>
<BLURB>Luma threshold.</BLURB>
<ARG>
<NAME>GstRadioacTV::interval</NAME>
<TYPE>guint</TYPE>
-<RANGE><= G_MAXINT</RANGE>
+<RANGE><= G_MAXLONG</RANGE>
<FLAGS>rw</FLAGS>
<NICK>Interval</NICK>
<BLURB>Snapshot interval (in strobe mode).</BLURB>
<ARG>
<NAME>GstRtpSession::rtcp-rr-bandwidth</NAME>
<TYPE>gint</TYPE>
-<RANGE>>= G_MAXULONG</RANGE>
+<RANGE>>= -1</RANGE>
<FLAGS>rw</FLAGS>
<NICK>RTCP RR bandwidth</NICK>
<BLURB>The RTCP bandwidth used for receivers in bytes per second (-1 = default).</BLURB>
<ARG>
<NAME>GstRtpSession::rtcp-rs-bandwidth</NAME>
<TYPE>gint</TYPE>
-<RANGE>>= G_MAXULONG</RANGE>
+<RANGE>>= -1</RANGE>
<FLAGS>rw</FLAGS>
<NICK>RTCP RS bandwidth</NICK>
<BLURB>The RTCP bandwidth used for senders in bytes per second (-1 = default).</BLURB>
<ARG>
<NAME>GstV4l2Sink::device-fd</NAME>
<TYPE>gint</TYPE>
-<RANGE>>= G_MAXULONG</RANGE>
+<RANGE>>= -1</RANGE>
<FLAGS>r</FLAGS>
<NICK>File descriptor</NICK>
<BLURB>File descriptor of the device.</BLURB>
GObject
+ GdkPixbuf
+ GstCmmlTagClip
+ GstCmmlTagHead
+ GstCmmlTagStream
+ GstColorBalanceChannel
GstObject
- GstPad
- GstVideoMixer2Pad
- GstVideoMixerPad
- GstInterleavePad
- GstPadTemplate
- GstPluginFeature
- GstElementFactory
- GstTypeFindFactory
- GstIndexFactory
+ GstBus
+ GstClock
+ GstSystemClock
+ GstAudioClock
GstElement
- GstBin
- GstPipeline
- GstQTMoovRecover
- GstSwitchSink
- GstGConfVideoSink
- GstGConfAudioSink
- GstSwitchSrc
- GstGConfVideoSrc
- GstGConfAudioSrc
- GstHalAudioSink
- GstHalAudioSrc
- GstRtpBin
- GstAutoVideoSink
- GstAutoVideoSrc
- GstAutoAudioSink
- GstAutoAudioSrc
- GstPushFileSrc
- GstRTSPSrc
- GstRgVolume
- GstAspectRatioCrop
- GstCmmlEnc
- GstCmmlDec
- GstBaseSink
- GstAASink
- GstBaseAudioSink
- GstPulseSink
- GstJackAudioSink
- GstAudioSink
- GstEsdSink
- GstOssSink
- GstOss4Sink
- GstCACASink
- GstVideoSink
- GstGdkPixbufSink
- GstShout2send
- GstTest
- GstMultiFileSink
- GstMultiUDPSink
- GstUDPSink
- GstDynUDPSink
- GstBaseSrc
- GstPushSrc
- GstDV1394Src
- GstHDV1394Src
- GstSoupHTTPSrc
- GstBaseAudioSrc
- GstAudioSrc
- GstPulseSrc
- GstOssSrc
- GstOss4Source
- GstJackAudioSrc
- GstV4l2Src
- GstXImageSrc
- GstMultiFileSrc
- GstUDPSrc
- GstWavpackParse
- GstWavpackDec
- GstWavpackEnc
- GstDVDemux
- GstDVDec
- GstTagLibMux
- GstId3v2Mux
- GstApev2Mux
- GstFlacEnc
- GstFlacDec
- GstFlacTag
- GstCairoTextOverlay
- GstBaseTransform
- GstCairoTimeOverlay
- GstVideoFilter
- GstCairoOverlay
- GstEdgeTV
- GstAgingTV
- GstDiceTV
- GstWarpTV
- GstShagadelicTV
- GstVertigoTV
- GstRevTV
- GstQuarkTV
- GstOpTV
- GstRadioacTV
- GstStreakTV
- GstRippleTV
- GstNavigationtest
- GstGamma
- GstVideoBalance
- GstVideoFlip
- GstSMPTEAlpha
- GstAlpha
- GstAlphaColor
- GstPixbufScale
- GstVideoBox
- GstBreakMyData
- GstCapsSetter
- GstNavSeek
- GstProgressReport
- GstTagInject
- GstCpuReport
- GstLevel
- GstAudioFilter
- GstIirEqualizer
- GstIirEqualizerNBands
- GstIirEqualizer3Bands
- GstIirEqualizer10Bands
- GstSpectrum
- GstAudioInvert
- GstAudioKaraoke
- GstAudioAmplify
- GstAudioDynamic
- GstAudioFXBaseIIRFilter
- GstAudioChebLimit
- GstAudioChebBand
- GstAudioIIRFilter
- GstAudioFXBaseFIRFilter
- GstAudioWSincLimit
- GstAudioWSincBand
- GstAudioFIRFilter
- GstAudioEcho
- GstRgAnalysis
- GstRgLimiter
- GstVideoCrop
- GstAudioPanorama
- GstCairoRender
- GstPulseMixer
- GstSpeexEnc
- GstSpeexDec
- GstJpegEnc
- GstJpegDec
- GstSmokeEnc
- GstSmokeDec
- GstPngDec
- GstPngEnc
- GstGdkPixbuf
- GstOssMixerElement
- GstV4l2Radio
- GstOss4Mixer
- GstShapeWipe
+ Gst3GPPMux
+ GstALawDec
+ GstALawEnc
+ GstAsteriskh263
+ GstAuParse
GstAviDemux
GstAviMux
GstAviSubtitle
- GstRTPDepay
+ GstBaseParse
+ GstAacParse
+ GstAc3Parse
+ GstAmrParse
+ GstDcaParse
+ GstFlacParse
+ GstMpegAudioParse
GstBaseRTPDepayload
- GstRtpAC3Depay
GstRTPBVDepay
- GstRtpCELTDepay
GstRTPDVDepay
- GstRtpGSTDepay
+ GstRTPGSMDepay
+ GstRTPSirenDepay
GstRTPiLBCDepay
+ GstRtpAC3Depay
+ GstRtpAMRDepay
+ GstRtpCELTDepay
GstRtpG722Depay
GstRtpG723Depay
GstRtpG726Depay
GstRtpG729Depay
- GstRTPGSMDepay
- GstRtpAMRDepay
- GstRtpPcmaDepay
- GstRtpPcmuDepay
- GstRtpMPADepay
- GstRtpMPARobustDepay
- GstRtpMPVDepay
- GstRtpH263PDepay
+ GstRtpGSTDepay
GstRtpH263Depay
+ GstRtpH263PDepay
GstRtpH264Depay
GstRtpJ2KDepay
GstRtpJPEGDepay
GstRtpL16Depay
GstRtpMP1SDepay
GstRtpMP2TDepay
- GstRtpMP4VDepay
GstRtpMP4ADepay
GstRtpMP4GDepay
+ GstRtpMP4VDepay
+ GstRtpMPADepay
+ GstRtpMPARobustDepay
+ GstRtpMPVDepay
+ GstRtpPcmaDepay
+ GstRtpPcmuDepay
GstRtpQCELPDepay
GstRtpQDM2Depay
- GstRTPSirenDepay
GstRtpSPEEXDepay
GstRtpSV3VDepay
GstRtpTheoraDepay
- GstRtpVorbisDepay
GstRtpVRawDepay
+ GstRtpVorbisDepay
GstRtpXQTDepay
GstBaseRTPPayload
- GstRtpAC3Pay
GstBaseRTPAudioPayload
GstRTPBVPay
GstRTPILBCPay
+ GstRTPSirenPay
GstRtpG722Pay
GstRtpG726Pay
- GstRtpPcmuPay
- GstRtpPcmaPay
GstRtpL16Pay
- GstRTPSirenPay
- GstRtpCELTPay
+ GstRtpPcmaPay
+ GstRtpPcmuPay
GstRTPDVPay
- GstRtpGSTPay
GstRTPG723Pay
GstRTPG729Pay
GstRTPGSMPay
- GstRtpAMRPay
- GstRtpMPAPay
+ GstRTPMP2TPay
GstRTPMPVPay
+ GstRtpAC3Pay
+ GstRtpAMRPay
+ GstRtpCELTPay
+ GstRtpGSTPay
GstRtpH263PPay
GstRtpH263Pay
GstRtpH264Pay
GstRtpJ2KPay
GstRtpJPEGPay
- GstRTPMP2TPay
- GstRtpMP4VPay
GstRtpMP4APay
GstRtpMP4GPay
+ GstRtpMP4VPay
+ GstRtpMPAPay
GstRtpSPEEXPay
GstRtpTheoraPay
- GstRtpVorbisPay
GstRtpVRawPay
- GstAsteriskh263
- GstGoom
- GstGoom2k1
- GstWavEnc
- GstRtpJitterBuffer
- GstRtpPtDemux
- GstRtpSession
- GstRtpSsrcDemux
- GstRndBufferSize
+ GstRtpVorbisPay
+ GstBaseSink
+ GstAASink
+ GstBaseAudioSink
+ GstAudioSink
+ GstEsdSink
+ GstOss4Sink
+ GstOssSink
+ GstJackAudioSink
+ GstPulseSink
+ GstCACASink
+ GstDynUDPSink
+ GstMultiFileSink
+ GstMultiUDPSink
+ GstUDPSink
+ GstShout2send
+ GstTest
+ GstVideoSink
+ GstGdkPixbufSink
+ GstV4l2Sink
+ GstBaseSrc
+ GstPushSrc
+ GstBaseAudioSrc
+ GstAudioSrc
+ GstOss4Source
+ GstOssSrc
+ GstPulseSrc
+ GstJackAudioSrc
+ GstDV1394Src
+ GstHDV1394Src
+ GstMultiFileSrc
+ GstSoupHTTPSrc
+ GstUDPSrc
+ GstV4l2Src
+ GstXImageSrc
+ GstBaseTransform
+ GstAudioFilter
+ GstAudioAmplify
+ GstAudioDynamic
+ GstAudioEcho
+ GstAudioFXBaseFIRFilter
+ GstAudioFIRFilter
+ GstAudioWSincBand
+ GstAudioWSincLimit
+ GstAudioFXBaseIIRFilter
+ GstAudioChebBand
+ GstAudioChebLimit
+ GstAudioIIRFilter
+ GstAudioInvert
+ GstAudioKaraoke
+ GstIirEqualizer
+ GstIirEqualizer10Bands
+ GstIirEqualizer3Bands
+ GstIirEqualizerNBands
+ GstSpectrum
+ GstAudioPanorama
+ GstBreakMyData
+ GstCairoTimeOverlay
+ GstCapsSetter
+ GstCpuReport
+ GstLevel
+ GstNavSeek
+ GstPixbufScale
+ GstProgressReport
+ GstRgAnalysis
+ GstRgLimiter
+ GstTagInject
+ GstVideoBox
+ GstVideoCrop
+ GstVideoFilter
+ GstAgingTV
+ GstAlpha
+ GstAlphaColor
+ GstCairoOverlay
+ GstDiceTV
+ GstEdgeTV
+ GstGamma
+ GstNavigationtest
+ GstOpTV
+ GstQuarkTV
+ GstRadioacTV
+ GstRevTV
+ GstRippleTV
+ GstSMPTEAlpha
+ GstShagadelicTV
+ GstStreakTV
+ GstVertigoTV
+ GstVideoBalance
+ GstVideoFlip
+ GstWarpTV
+ GstBin
+ GstAspectRatioCrop
+ GstAutoAudioSink
+ GstAutoAudioSrc
+ GstAutoVideoSink
+ GstAutoVideoSrc
+ GstHalAudioSink
+ GstHalAudioSrc
+ GstPipeline
+ GstQTMoovRecover
+ GstPushFileSrc
+ GstRTSPSrc
+ GstRgVolume
+ GstRtpBin
+ GstSwitchSink
+ GstGConfAudioSink
+ GstGConfVideoSink
+ GstSwitchSrc
+ GstGConfAudioSrc
+ GstGConfVideoSrc
+ GstCairoRender
+ GstCairoTextOverlay
GstCapsDebug
- GstEFence
+ GstCmmlDec
+ GstCmmlEnc
GstCutter
+ GstDVDec
+ GstDVDemux
+ GstDeinterlace
+ GstDeinterleave
+ GstEFence
+ GstFlacDec
+ GstFlacEnc
+ GstFlacTag
+ GstFlvDemux
+ GstFlvMux
+ GstFlxDec
+ GstGPPMux
+ GstGdkPixbuf
+ GstGoom
+ GstGoom2k1
+ GstICYDemux
+ GstISMLMux
+ GstImageFreeze
+ GstInterleave
+ GstJpegDec
+ GstJpegEnc
+ GstMJ2Mux
+ GstMP4Mux
GstMatroskaDemux
- GstMatroskaParse
GstMatroskaMux
GstWebMMux
- GstRTPDec
- GstSMPTE
- GstAuParse
+ GstMatroskaParse
+ GstMonoscope
+ GstMuLawDec
+ GstMuLawEnc
GstMultipartDemux
GstMultipartMux
- GstALawEnc
- GstALawDec
- GstMuLawEnc
- GstMuLawDec
+ GstOss4Mixer
+ GstOssMixerElement
+ GstPngDec
+ GstPngEnc
+ GstPulseMixer
+ GstQTDemux
+ GstQTMux
+ GstRTPDec
+ GstRTPDepay
+ GstRndBufferSize
+ GstRtpJitterBuffer
+ GstRtpPtDemux
+ GstRtpSession
+ GstRtpSsrcDemux
+ GstSMPTE
+ GstShapeWipe
+ GstSmokeDec
+ GstSmokeEnc
+ GstSpeexDec
+ GstSpeexEnc
GstTagDemux
GstApeDemux
GstID3Demux
- GstFlxDec
- GstDeinterlace
- GstImageFreeze
- GstBaseParse
- GstAacParse
- GstAmrParse
- GstAc3Parse
- GstDcaParse
- GstFlacParse
- GstMpegAudioParse
- GstY4mEncode
- GstInterleave
- GstDeinterleave
- GstWavParse
- GstFlvDemux
- GstFlvMux
- GstQTDemux
- GstQTMux
- GstMP4Mux
- GstISMLMux
- Gst3GPPMux
- GstGPPMux
- GstMJ2Mux
- GstICYDemux
+ GstTagLibMux
+ GstApev2Mux
+ GstId3v2Mux
+ GstV4l2Radio
GstVideoMixer
GstVideoMixer2
- GstBus
- GstTask
- GstTaskPool
- GstClock
- GstSystemClock
- GstAudioClock
+ GstWavEnc
+ GstWavParse
+ GstWavpackDec
+ GstWavpackEnc
+ GstWavpackParse
+ GstY4mEncode
+ GstPad
+ GstInterleavePad
+ GstVideoMixer2Pad
+ GstVideoMixerPad
+ GstPadTemplate
GstPlugin
+ GstPluginFeature
+ GstElementFactory
+ GstIndexFactory
+ GstTypeFindFactory
GstRegistry
GstRingBuffer
- GstAudioSrcRingBuffer
GstAudioSinkRingBuffer
- GstJackAudioSrcRingBuffer
+ GstAudioSrcRingBuffer
GstJackAudioSinkRingBuffer
+ GstJackAudioSrcRingBuffer
+ GstTask
+ GstTaskPool
GstSignalObject
- GstCmmlTagStream
- GstCmmlTagHead
- GstCmmlTagClip
- GstColorBalanceChannel
- RTPSession
- GstTunerNorm
GstTunerChannel
- GdkPixbuf
+ GstTunerNorm
+ RTPSession
GInterface
+ GIcon
GTypePlugin
GstChildProxy
- GstURIHandler
- GstPropertyProbe
- GstPreset
- GstTagSetter
- GstStreamVolume
+ GstColorBalance
GstImplementsInterface
GstMixer
+ GstNavigation
+ GstPreset
+ GstPropertyProbe
+ GstStreamVolume
+ GstTagSetter
+ GstTagXmpWriter
GstTuner
- GstColorBalance
+ GstURIHandler
GstVideoOrientation
- GstTagXmpWriter
- GIcon
+ GstXOverlay
+GdkPixbuf GIcon
+Gst3GPPMux GstTagSetter GstTagXmpWriter
+GstApev2Mux GstTagSetter
+GstAspectRatioCrop GstChildProxy
+GstAutoAudioSink GstChildProxy
+GstAutoAudioSrc GstChildProxy
+GstAutoVideoSink GstChildProxy
+GstAutoVideoSrc GstChildProxy
+GstAviMux GstTagSetter
GstBin GstChildProxy
-GstPipeline GstChildProxy
-GstQTMoovRecover GstChildProxy
-GstSwitchSink GstChildProxy
-GstGConfVideoSink GstChildProxy
+GstDV1394Src GstURIHandler GstPropertyProbe
+GstDeinterlace GstChildProxy
+GstFlacEnc GstTagSetter GstPreset
+GstFlacTag GstTagSetter
+GstFlvMux GstTagSetter
GstGConfAudioSink GstChildProxy
-GstSwitchSrc GstChildProxy
-GstGConfVideoSrc GstChildProxy
GstGConfAudioSrc GstChildProxy
+GstGConfVideoSink GstChildProxy
+GstGConfVideoSrc GstChildProxy
+GstGPPMux GstTagSetter GstTagXmpWriter
+GstHDV1394Src GstURIHandler GstPropertyProbe
GstHalAudioSink GstChildProxy
GstHalAudioSrc GstChildProxy
-GstRtpBin GstChildProxy
-GstAutoVideoSink GstChildProxy
-GstAutoVideoSrc GstChildProxy
-GstAutoAudioSink GstChildProxy
-GstAutoAudioSrc GstChildProxy
+GstISMLMux GstTagSetter GstTagXmpWriter
+GstId3v2Mux GstTagSetter
+GstIirEqualizer GstChildProxy
+GstIirEqualizer10Bands GstChildProxy GstPreset
+GstIirEqualizer3Bands GstChildProxy GstPreset
+GstIirEqualizerNBands GstChildProxy
+GstMJ2Mux GstTagSetter GstTagXmpWriter
+GstMP4Mux GstTagSetter GstTagXmpWriter
+GstMatroskaMux GstTagSetter
+GstOss4Mixer GstImplementsInterface GstMixer GstPropertyProbe
+GstOss4Sink GstStreamVolume GstPropertyProbe
+GstOss4Source GstImplementsInterface GstMixer GstPropertyProbe
+GstOssMixerElement GstImplementsInterface GstMixer
+GstOssSrc GstImplementsInterface GstMixer
+GstPipeline GstChildProxy
+GstPulseMixer GstImplementsInterface GstMixer GstPropertyProbe
+GstPulseSink GstStreamVolume GstImplementsInterface GstPropertyProbe
+GstPulseSrc GstImplementsInterface GstMixer GstPropertyProbe
GstPushFileSrc GstChildProxy GstURIHandler
+GstQTMoovRecover GstChildProxy
+GstQTMux GstTagSetter GstTagXmpWriter
GstRTSPSrc GstChildProxy GstURIHandler
GstRgVolume GstChildProxy
-GstAspectRatioCrop GstChildProxy
-GstPulseSink GstStreamVolume GstImplementsInterface GstPropertyProbe
-GstOss4Sink GstStreamVolume GstPropertyProbe
+GstRtpBin GstChildProxy
GstShout2send GstTagSetter
-GstUDPSink GstURIHandler
-GstDV1394Src GstURIHandler GstPropertyProbe
-GstHDV1394Src GstURIHandler GstPropertyProbe
GstSoupHTTPSrc GstURIHandler
-GstPulseSrc GstImplementsInterface GstMixer GstPropertyProbe
-GstOssSrc GstImplementsInterface GstMixer
-GstOss4Source GstImplementsInterface GstMixer GstPropertyProbe
-GstV4l2Src GstURIHandler GstImplementsInterface GstTuner GstColorBalance GstVideoOrientation GstPropertyProbe
-GstUDPSrc GstURIHandler
-GstWavpackEnc GstPreset
-GstTagLibMux GstTagSetter
-GstId3v2Mux GstTagSetter
-GstApev2Mux GstTagSetter
-GstFlacEnc GstTagSetter GstPreset
-GstFlacTag GstTagSetter
-GstVideoBalance GstImplementsInterface GstColorBalance
-GstIirEqualizer GstChildProxy
-GstIirEqualizerNBands GstChildProxy
-GstIirEqualizer3Bands GstChildProxy GstPreset
-GstIirEqualizer10Bands GstChildProxy GstPreset
-GstPulseMixer GstImplementsInterface GstMixer GstPropertyProbe
GstSpeexEnc GstTagSetter GstPreset
-GstOssMixerElement GstImplementsInterface GstMixer
+GstSwitchSink GstChildProxy
+GstSwitchSrc GstChildProxy
+GstTagLibMux GstTagSetter
+GstUDPSink GstURIHandler
+GstUDPSrc GstURIHandler
GstV4l2Radio GstURIHandler GstImplementsInterface GstTuner GstPropertyProbe
-GstOss4Mixer GstImplementsInterface GstMixer GstPropertyProbe
-GstAviMux GstTagSetter
-GstMatroskaMux GstTagSetter
-GstWebMMux GstTagSetter
-GstDeinterlace GstChildProxy
-GstFlvMux GstTagSetter
-GstQTMux GstTagSetter GstTagXmpWriter
-GstMP4Mux GstTagSetter GstTagXmpWriter
-GstISMLMux GstTagSetter GstTagXmpWriter
-Gst3GPPMux GstTagSetter GstTagXmpWriter
-GstGPPMux GstTagSetter GstTagXmpWriter
-GstMJ2Mux GstTagSetter GstTagXmpWriter
+GstV4l2Sink GstImplementsInterface GstXOverlay GstNavigation GstColorBalance GstVideoOrientation GstPropertyProbe
+GstV4l2Src GstURIHandler GstImplementsInterface GstTuner GstColorBalance GstVideoOrientation GstPropertyProbe
+GstVideoBalance GstImplementsInterface GstColorBalance
GstVideoMixer GstChildProxy
GstVideoMixer2 GstChildProxy
-GdkPixbuf GIcon
+GstWavpackEnc GstPreset
+GstWebMMux GstTagSetter
+GIcon GObject
GstChildProxy GstObject
-GstTagSetter GstElement
-GstStreamVolume GObject
+GstColorBalance GstImplementsInterface GstElement
GstImplementsInterface GstElement
GstMixer GstImplementsInterface GstElement
+GstStreamVolume GObject
+GstTagSetter GstElement
+GstTagXmpWriter GstElement
GstTuner GstImplementsInterface GstElement
-GstColorBalance GstImplementsInterface GstElement
GstVideoOrientation GstImplementsInterface GstElement
-GstTagXmpWriter GstElement
-GIcon GObject
+GstXOverlay GstImplementsInterface GstElement
gst_query_parse_duration (query, &fmt, NULL);
- /* try any demuxers before us first */
- if (fmt == GST_FORMAT_TIME && peer && gst_pad_query (peer, query)) {
+ /* try any demuxers or parsers before us first */
+ if ((fmt == GST_FORMAT_TIME || fmt == GST_FORMAT_DEFAULT) &&
+ peer != NULL && gst_pad_query (peer, query)) {
gst_query_parse_duration (query, NULL, &len);
GST_DEBUG_OBJECT (flacdec, "peer returned duration %" GST_TIME_FORMAT,
GST_TIME_ARGS (len));
GstBaseParseFrame * frame);
static GstFlowReturn gst_flac_parse_pre_push_frame (GstBaseParse * parse,
GstBaseParseFrame * frame);
+static gboolean gst_flac_parse_convert (GstBaseParse * parse,
+ GstFormat src_format, gint64 src_value, GstFormat dest_format,
+ gint64 * dest_value);
GST_BOILERPLATE (GstFlacParse, gst_flac_parse, GstBaseParse,
GST_TYPE_BASE_PARSE);
baseparse_class->parse_frame = GST_DEBUG_FUNCPTR (gst_flac_parse_parse_frame);
baseparse_class->pre_push_frame =
GST_DEBUG_FUNCPTR (gst_flac_parse_pre_push_frame);
+ baseparse_class->convert = GST_DEBUG_FUNCPTR (gst_flac_parse_convert);
}
static void
if (!gst_bit_reader_get_bits_uint16 (&reader, &flacparse->min_blocksize, 16))
goto error;
if (flacparse->min_blocksize < 16) {
- GST_ERROR_OBJECT (flacparse, "Invalid minimum block size: %u",
+ GST_WARNING_OBJECT (flacparse, "Invalid minimum block size: %u",
flacparse->min_blocksize);
- return FALSE;
}
if (!gst_bit_reader_get_bits_uint16 (&reader, &flacparse->max_blocksize, 16))
goto error;
if (flacparse->max_blocksize < 16) {
- GST_ERROR_OBJECT (flacparse, "Invalid maximum block size: %u",
+ GST_WARNING_OBJECT (flacparse, "Invalid maximum block size: %u",
flacparse->max_blocksize);
- return FALSE;
}
if (!gst_bit_reader_get_bits_uint32 (&reader, &flacparse->min_framesize, 24))
if (!gst_bit_reader_get_bits_uint64 (&reader, &flacparse->total_samples, 36))
goto error;
- if (flacparse->total_samples)
- gst_base_parse_set_duration (GST_BASE_PARSE (flacparse), GST_FORMAT_TIME,
- GST_FRAMES_TO_CLOCK_TIME (flacparse->total_samples,
- flacparse->samplerate), 0);
+ if (flacparse->total_samples) {
+ gst_base_parse_set_duration (GST_BASE_PARSE (flacparse),
+ GST_FORMAT_DEFAULT, flacparse->total_samples, 0);
+ }
GST_DEBUG_OBJECT (flacparse, "STREAMINFO:\n"
"\tmin/max blocksize: %u/%u,\n"
return GST_FLOW_OK;
}
+
+static gboolean
+gst_flac_parse_convert (GstBaseParse * parse,
+ GstFormat src_format, gint64 src_value, GstFormat dest_format,
+ gint64 * dest_value)
+{
+ GstFlacParse *flacparse = GST_FLAC_PARSE (parse);
+
+ if (flacparse->samplerate > 0) {
+ if (src_format == GST_FORMAT_DEFAULT && dest_format == GST_FORMAT_TIME) {
+ if (src_value != -1)
+ *dest_value =
+ gst_util_uint64_scale (src_value, GST_SECOND,
+ flacparse->samplerate);
+ else
+ *dest_value = -1;
+ return TRUE;
+ } else if (src_format == GST_FORMAT_TIME &&
+ dest_format == GST_FORMAT_DEFAULT) {
+ if (src_value != -1)
+ *dest_value =
+ gst_util_uint64_scale (src_value, flacparse->samplerate,
+ GST_SECOND);
+ else
+ *dest_value = -1;
+ return TRUE;
+ }
+ }
+
+ return GST_BASE_PARSE_CLASS (parent_class)->convert (parse, src_format,
+ src_value, dest_format, dest_value);
+}
static gboolean gst_au_parse_src_query (GstPad * pad, GstQuery * query);
static gboolean gst_au_parse_src_event (GstPad * pad, GstEvent * event);
static gboolean gst_au_parse_sink_event (GstPad * pad, GstEvent * event);
+static gboolean gst_au_parse_src_convert (GstAuParse * auparse,
+ GstFormat src_format, gint64 srcval, GstFormat dest_format,
+ gint64 * destval);
GST_BOILERPLATE (GstAuParse, gst_au_parse, GstElement, GST_TYPE_ELEMENT);
}
auparse->offset = GST_READ_UINT32_BE (head + 4);
- /* Do not trust size, could be set to -1 : unknown */
+ /* Do not trust size, could be set to -1 : unknown
+ * otherwise: filesize = size + auparse->offset
+ */
size = GST_READ_UINT32_BE (head + 8);
auparse->encoding = GST_READ_UINT32_BE (head + 12);
auparse->samplerate = GST_READ_UINT32_BE (head + 16);
GstFlowReturn ret = GST_FLOW_OK;
GstAuParse *auparse;
gint avail, sendnow = 0;
+ gint64 timestamp;
+ gint64 duration;
+ gint64 offset;
auparse = GST_AU_PARSE (gst_pad_get_parent (pad));
goto out;
gst_pad_push_event (auparse->srcpad,
- gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_DEFAULT,
+ gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
0, GST_CLOCK_TIME_NONE, 0));
}
if (sendnow > 0) {
GstBuffer *outbuf;
const guint8 *data;
+ gint64 pos;
ret = gst_pad_alloc_buffer_and_set_caps (auparse->srcpad,
auparse->buffer_offset, sendnow, GST_PAD_CAPS (auparse->srcpad),
memcpy (GST_BUFFER_DATA (outbuf), data, sendnow);
gst_adapter_flush (auparse->adapter, sendnow);
+ pos = auparse->buffer_offset - auparse->offset;
+ pos = MAX (pos, 0);
+
+ if (auparse->sample_size > 0 && auparse->samplerate > 0) {
+ gst_au_parse_src_convert (auparse, GST_FORMAT_BYTES, pos,
+ GST_FORMAT_DEFAULT, &offset);
+ gst_au_parse_src_convert (auparse, GST_FORMAT_BYTES, pos,
+ GST_FORMAT_TIME, ×tamp);
+ gst_au_parse_src_convert (auparse, GST_FORMAT_BYTES,
+ sendnow, GST_FORMAT_TIME, &duration);
+
+ GST_BUFFER_OFFSET (outbuf) = offset;
+ GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
+ GST_BUFFER_DURATION (outbuf) = duration;
+ }
+
auparse->buffer_offset += sendnow;
ret = gst_pad_push (auparse->srcpad, outbuf);
/* fallthrough */
case GST_FORMAT_DEFAULT:{
switch (dest_format) {
+ case GST_FORMAT_DEFAULT:
+ *destval = srcval;
+ break;
case GST_FORMAT_BYTES:
*destval = srcval * samplesize;
break;
case GST_FORMAT_TIME:{
switch (dest_format) {
case GST_FORMAT_BYTES:
- *destval =
- gst_util_uint64_scale_int (srcval, rate * samplesize, GST_SECOND);
+ *destval = samplesize *
+ gst_util_uint64_scale_int (srcval, rate, GST_SECOND);
break;
case GST_FORMAT_DEFAULT:
*destval = gst_util_uint64_scale_int (srcval, rate, GST_SECOND);
len -= auparse->offset;
GST_OBJECT_UNLOCK (auparse);
- ret = gst_au_parse_src_convert (auparse, GST_FORMAT_BYTES, len,
- format, &val);
+ ret = gst_au_parse_src_convert (auparse, bformat, len, format, &val);
if (ret) {
gst_query_set_duration (query, format, val);
}
break;
}
+ case GST_QUERY_SEEKING:{
+ GstFormat format;
+
+ gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
+ /* FIXME: query duration in 'format'
+ gst_query_set_seeking (query, format, TRUE, 0, duration);
+ */
+ gst_query_set_seeking (query, format, TRUE, 0, GST_CLOCK_TIME_NONE);
+ ret = TRUE;
+ break;
+ }
default:
ret = gst_pad_query_default (pad, query);
break;
GstFormat format;
gdouble rate;
gint64 start, stop;
+ gboolean res;
gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
&stop_type, &stop);
return FALSE;
}
- /* FIXME: implement seeking */
- return FALSE;
+ res = gst_au_parse_src_convert (auparse, GST_FORMAT_TIME, start,
+ GST_FORMAT_BYTES, &start);
+
+ if (stop > 0) {
+ res = gst_au_parse_src_convert (auparse, GST_FORMAT_TIME, stop,
+ GST_FORMAT_BYTES, &stop);
+ }
+
+ GST_INFO_OBJECT (auparse,
+ "seeking: %" G_GINT64_FORMAT " ... %" G_GINT64_FORMAT, start, stop);
+
+ event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, start_type, start,
+ stop_type, stop);
+ res = gst_pad_push_event (auparse->sinkpad, event);
+ return res;
}
static gboolean
gst_au_parse_sink_event (GstPad * pad, GstEvent * event)
{
GstAuParse *auparse;
- gboolean ret;
+ gboolean ret = TRUE;
auparse = GST_AU_PARSE (gst_pad_get_parent (pad));
switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_NEWSEGMENT:
+ {
+ GstFormat format;
+ gdouble rate, arate;
+ gint64 start, stop, time, offset = 0;
+ gboolean update;
+ GstSegment segment;
+ GstEvent *new_event = NULL;
+
+ 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);
+
+ if (auparse->sample_size > 0) {
+ if (start > 0) {
+ offset = start;
+ start -= auparse->offset;
+ start = MAX (start, 0);
+ }
+ if (stop > 0) {
+ stop -= auparse->offset;
+ stop = MAX (stop, 0);
+ }
+ gst_au_parse_src_convert (auparse, GST_FORMAT_BYTES, start,
+ GST_FORMAT_TIME, &start);
+ gst_au_parse_src_convert (auparse, GST_FORMAT_BYTES, stop,
+ GST_FORMAT_TIME, &stop);
+ }
+
+ if (auparse->srcpad) {
+ GST_INFO_OBJECT (auparse,
+ "new segment: %" GST_TIME_FORMAT " ... %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
+
+ new_event = gst_event_new_new_segment_full (update, rate, arate,
+ GST_FORMAT_TIME, start, stop, start);
+
+ ret = gst_pad_push_event (auparse->srcpad, new_event);
+ }
+
+ auparse->buffer_offset = offset;
+
+ gst_event_unref (event);
+ break;
+ }
default:
ret = gst_pad_event_default (pad, event);
break;
data = gst_buffer_map (obuf, NULL, NULL, GST_MAP_WRITE);
gst_buffer_extract (*buf, 0, data, size);
+ /* assume 0 padding, at least makes outcome deterministic */
+ data[size] = 0;
gst_buffer_unmap (obuf, data, size + 1);
gst_buffer_replace (buf, obuf);
}
gst_element_class_set_details_simple (gstelement_class, "Random buffer size",
"Testing", "pull random sized buffers",
- "Stefan Kost <stefan.kost@nokia.com>)");
+ "Stefan Kost <stefan.kost@nokia.com>");
}
* GStreamer
* Copyright (C) 2005 Martin Eikermann <meiker@upb.de>
* Copyright (C) 2008-2010 Sebastian Dröge <slomo@collabora.co.uk>
+ * Copyright (C) 2011 Robert Swain <robert.swain@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
#define DEFAULT_METHOD GST_DEINTERLACE_LINEAR
#define DEFAULT_FIELDS GST_DEINTERLACE_ALL
#define DEFAULT_FIELD_LAYOUT GST_DEINTERLACE_LAYOUT_AUTO
+#define DEFAULT_LOCKING GST_DEINTERLACE_LOCKING_NONE
+#define DEFAULT_IGNORE_OBSCURE TRUE
+#define DEFAULT_DROP_ORPHANS TRUE
enum
{
PROP_METHOD,
PROP_FIELDS,
PROP_FIELD_LAYOUT,
+ PROP_LOCKING,
+ PROP_IGNORE_OBSCURE,
+ PROP_DROP_ORPHANS,
PROP_LAST
};
+#define GST_DEINTERLACE_BUFFER_STATE_P (1<<0)
+#define GST_DEINTERLACE_BUFFER_STATE_I (1<<1)
+#define GST_DEINTERLACE_BUFFER_STATE_TC_B (1<<2)
+#define GST_DEINTERLACE_BUFFER_STATE_TC_T (1<<3)
+#define GST_DEINTERLACE_BUFFER_STATE_TC_P (1<<4)
+#define GST_DEINTERLACE_BUFFER_STATE_TC_M (1<<5)
+#define GST_DEINTERLACE_BUFFER_STATE_DROP (1<<6)
+
+#define GST_ONE \
+ (GST_DEINTERLACE_BUFFER_STATE_TC_T | GST_DEINTERLACE_BUFFER_STATE_TC_B)
+#define GST_PRG \
+ (GST_DEINTERLACE_BUFFER_STATE_P | GST_DEINTERLACE_BUFFER_STATE_TC_P)
+#define GST_INT \
+ (GST_DEINTERLACE_BUFFER_STATE_I | GST_DEINTERLACE_BUFFER_STATE_TC_M)
+#define GST_DRP (GST_DEINTERLACE_BUFFER_STATE_DROP)
+
+#define GST_DEINTERLACE_OBSCURE_THRESHOLD 5
+
+static const TelecinePattern telecine_patterns[] = {
+ /* 60i -> 60p or 50i -> 50p (NOTE THE WEIRD RATIOS) */
+ {"1:1", 1, 2, 1, {GST_ONE,}},
+ /* 60i -> 30p or 50i -> 25p */
+ {"2:2", 1, 1, 1, {GST_INT,}},
+ /* 60i telecine -> 24p */
+ {"2:3", 5, 4, 5, {GST_PRG, GST_PRG, GST_ONE, GST_ONE, GST_PRG,}},
+ {"3:2:2:3", 5, 4, 5, {GST_PRG, GST_ONE, GST_INT, GST_ONE, GST_PRG,}},
+ {"2:3:3:2", 5, 4, 5, {GST_PRG, GST_PRG, GST_DRP, GST_PRG, GST_PRG,}},
+
+ /* The following patterns are obscure and are ignored if ignore-obscure is
+ * set to true. If any patterns are added above this line, check and edit
+ * GST_DEINTERLACE_OBSCURE_THRESHOLD */
+
+ /* 50i Euro pulldown -> 24p */
+ {"2-11:3", 25, 24, 25, {GST_PRG, GST_PRG, GST_PRG, GST_PRG, GST_PRG,
+ GST_PRG, GST_PRG, GST_PRG, GST_PRG, GST_PRG,
+ GST_PRG, GST_PRG, GST_ONE, GST_INT, GST_INT,
+ GST_INT, GST_INT, GST_INT, GST_INT, GST_INT,
+ GST_INT, GST_INT, GST_INT, GST_ONE, GST_PRG,}},
+ /* 60i (NTSC 30000/1001) -> 16p (16000/1001) */
+ {"3:4-3", 15, 8, 15, {GST_PRG, GST_DRP, GST_PRG, GST_DRP, GST_PRG,
+ GST_DRP, GST_PRG, GST_DRP, GST_PRG, GST_DRP,
+ GST_PRG, GST_DRP, GST_PRG, GST_DRP, GST_PRG,}},
+ /* 50i (PAL) -> 16p */
+ {"3-7:4", 25, 16, 25, {GST_PRG, GST_DRP, GST_PRG, GST_PRG, GST_DRP,
+ GST_PRG, GST_PRG, GST_DRP, GST_PRG, GST_PRG,
+ GST_DRP, GST_PRG, GST_DRP, GST_PRG, GST_PRG,
+ GST_DRP, GST_PRG, GST_PRG, GST_DRP, GST_PRG,
+ GST_PRG, GST_DRP, GST_PRG, GST_PRG, GST_DRP,}},
+ /* NTSC 60i -> 18p */
+ {"3:3:4", 5, 3, 5, {GST_PRG, GST_DRP, GST_PRG, GST_DRP, GST_PRG,}},
+ /* NTSC 60i -> 20p */
+ {"3:3", 3, 2, 3, {GST_PRG, GST_DRP, GST_PRG,}},
+ /* NTSC 60i -> 27.5 */
+ {"3:2-4", 11, 10, 11, {GST_PRG, GST_PRG, GST_PRG, GST_PRG, GST_PRG,
+ GST_PRG, GST_ONE, GST_INT, GST_INT, GST_INT,
+ GST_ONE,}},
+ /* PAL 50i -> 27.5 */
+ {"1:2-4", 9, 9, 10, {GST_PRG, GST_PRG, GST_PRG, GST_PRG, GST_INT,
+ GST_INT, GST_INT, GST_INT, GST_INT,}},
+};
+
static const GEnumValue methods_types[] = {
{GST_DEINTERLACE_TOMSMOCOMP, "Motion Adaptive: Motion Search",
"tomsmocomp"},
{0, NULL, NULL},
};
+static const GEnumValue locking_types[] = {
+ {GST_DEINTERLACE_LOCKING_NONE,
+ "No pattern locking", "none"},
+ {GST_DEINTERLACE_LOCKING_AUTO,
+ "Choose passive/active locking depending on whether upstream is live",
+ "auto"},
+ {GST_DEINTERLACE_LOCKING_ACTIVE,
+ "Block until pattern-locked. Use accurate timestamp interpolation within a pattern repeat.",
+ "active"},
+ {GST_DEINTERLACE_LOCKING_PASSIVE,
+ "Do not block. Use naïve timestamp adjustment until pattern-locked based on state history.",
+ "passive"},
+ {0, NULL, NULL},
+};
+
#define GST_TYPE_DEINTERLACE_METHODS (gst_deinterlace_methods_get_type ())
static GType
return deinterlace_modes_type;
}
+#define GST_TYPE_DEINTERLACE_LOCKING (gst_deinterlace_locking_get_type ())
+static GType
+gst_deinterlace_locking_get_type (void)
+{
+ static GType deinterlace_locking_type = 0;
+
+ if (!deinterlace_locking_type) {
+ deinterlace_locking_type =
+ g_enum_register_static ("GstDeinterlaceLocking", locking_types);
+ }
+
+ return deinterlace_locking_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 ";" \
DEFAULT_FIELD_LAYOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
);
+ /**
+ * GstDeinterlace:locking
+ *
+ * This selects which approach to pattern locking is used which affects
+ * processing latency and accuracy of timestamp adjustment for telecine
+ * streams.
+ *
+ * Since: 0.10.29.
+ *
+ */
+ g_object_class_install_property (gobject_class, PROP_LOCKING,
+ g_param_spec_enum ("locking", "locking", "Pattern locking mode",
+ GST_TYPE_DEINTERLACE_LOCKING, DEFAULT_LOCKING,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstDeinterlace:ignore-obscure
+ *
+ * This selects whether to ignore obscure/rare telecine patterns.
+ * NTSC 2:3 pulldown variants are the only really common patterns.
+ *
+ * Since: 0.10.29.
+ *
+ */
+ g_object_class_install_property (gobject_class, PROP_IGNORE_OBSCURE,
+ g_param_spec_boolean ("ignore-obscure", "ignore-obscure",
+ "Ignore obscure telecine patterns (only consider P, I and 2:3 "
+ "variants).", DEFAULT_IGNORE_OBSCURE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstDeinterlace:drop-orphans
+ *
+ * This selects whether to drop orphan fields at the beginning of telecine
+ * patterns in active locking mode.
+ *
+ * Since: 0.10.29.
+ *
+ */
+ g_object_class_install_property (gobject_class, PROP_DROP_ORPHANS,
+ g_param_spec_boolean ("drop-orphans", "drop-orphans",
+ "Drop orphan fields at the beginning of telecine patterns in "
+ "active locking mode.", DEFAULT_DROP_ORPHANS,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
element_class->change_state =
GST_DEBUG_FUNCPTR (gst_deinterlace_change_state);
}
gst_deinterlace_set_method (self, self->user_set_method_id);
self->fields = DEFAULT_FIELDS;
self->field_layout = DEFAULT_FIELD_LAYOUT;
-
+ self->locking = DEFAULT_LOCKING;
+ self->ignore_obscure = DEFAULT_IGNORE_OBSCURE;
+ self->drop_orphans = DEFAULT_DROP_ORPHANS;
+
+ self->low_latency = -1;
+ self->pattern = -1;
+ self->pattern_phase = -1;
+ self->pattern_count = 0;
+ self->output_count = 0;
+ self->pattern_base_ts = GST_CLOCK_TIME_NONE;
+ self->pattern_buf_dur = GST_CLOCK_TIME_NONE;
self->still_frame_mode = FALSE;
gst_deinterlace_reset (self);
memset (self->field_history, 0,
GST_DEINTERLACE_MAX_FIELD_HISTORY * sizeof (GstDeinterlaceField));
self->history_count = 0;
+ memset (self->buf_states, 0,
+ GST_DEINTERLACE_MAX_BUFFER_STATE_HISTORY *
+ sizeof (GstDeinterlaceBufferState));
+ self->state_count = 0;
+ self->pattern_lock = FALSE;
+ self->pattern_refresh = TRUE;
if (!self->still_frame_mode && self->last_buffer) {
gst_buffer_unref (self->last_buffer);
gst_deinterlace_reset_history (self, TRUE);
gst_deinterlace_reset_qos (self);
+
+ self->need_more = FALSE;
+ self->have_eos = FALSE;
}
static void
case PROP_FIELD_LAYOUT:
self->field_layout = g_value_get_enum (value);
break;
+ case PROP_LOCKING:
+ self->locking = g_value_get_enum (value);
+ break;
+ case PROP_IGNORE_OBSCURE:
+ self->ignore_obscure = g_value_get_boolean (value);
+ break;
+ case PROP_DROP_ORPHANS:
+ self->drop_orphans = g_value_get_boolean (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
}
case PROP_FIELD_LAYOUT:
g_value_set_enum (value, self->field_layout);
break;
+ case PROP_LOCKING:
+ g_value_set_enum (value, self->locking);
+ break;
+ case PROP_IGNORE_OBSCURE:
+ g_value_set_boolean (value, self->ignore_obscure);
+ break;
+ case PROP_DROP_ORPHANS:
+ g_value_set_boolean (value, self->drop_orphans);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
+static void
+gst_deinterlace_update_pattern_timestamps (GstDeinterlace * self)
+{
+ gint state_idx;
+ if (self->low_latency) {
+ /* in low-latency mode the buffer state history contains old buffer
+ * states as well as the current one and perhaps some future ones.
+ * the current buffer's state is given by the number of field pairs
+ * rounded up, minus 1. the below is equivalent */
+ state_idx = (self->history_count - 1) >> 1;
+ } else {
+ /* in high-latency mode state_count - 1 is the current buffer's state */
+ state_idx = self->state_count - 1;
+ }
+
+ self->pattern_base_ts = self->buf_states[state_idx].timestamp;
+ self->pattern_buf_dur =
+ (self->buf_states[state_idx].duration *
+ telecine_patterns[self->pattern].ratio_d) /
+ telecine_patterns[self->pattern].ratio_n;
+ GST_DEBUG_OBJECT (self,
+ "Starting a new pattern repeat with base ts %" GST_TIME_FORMAT
+ " and dur %" GST_TIME_FORMAT, GST_TIME_ARGS (self->pattern_base_ts),
+ GST_TIME_ARGS (self->pattern_buf_dur));
+}
+
static GstBuffer *
gst_deinterlace_pop_history (GstDeinterlace * self)
{
buffer = self->field_history[self->history_count - 1].buf;
self->history_count--;
+ if (self->locking != GST_DEINTERLACE_LOCKING_NONE && (!self->history_count
+ || GST_BUFFER_DATA (buffer) !=
+ GST_BUFFER_DATA (self->field_history[self->history_count - 1].buf))) {
+ if (!self->low_latency)
+ self->state_count--;
+ if (self->pattern_lock) {
+ self->pattern_count++;
+ if (self->pattern != -1
+ && self->pattern_count >= telecine_patterns[self->pattern].length) {
+ self->pattern_count = 0;
+ self->output_count = 0;
+ gst_deinterlace_update_pattern_timestamps (self);
+ }
+ }
+ }
GST_DEBUG_OBJECT (self, "Returning buffer: %p %" GST_TIME_FORMAT
" with duration %" GST_TIME_FORMAT " and size %u", buffer,
return buffer;
}
+typedef enum
+{
+ GST_DEINTERLACE_PROGRESSIVE,
+ GST_DEINTERLACE_INTERLACED,
+ GST_DEINTERLACE_TELECINE,
+} GstDeinterlaceInterlacingMethod;
+
+static GstDeinterlaceInterlacingMethod
+gst_deinterlace_get_interlacing_method (const GstCaps * caps)
+{
+ GstDeinterlaceInterlacingMethod method = 0;
+ gboolean interlaced;
+
+ /* check interlaced cap */
+ gst_structure_get_boolean (gst_caps_get_structure (caps, 0), "interlaced",
+ &interlaced);
+
+ method =
+ interlaced ? GST_DEINTERLACE_INTERLACED : GST_DEINTERLACE_PROGRESSIVE;
+
+ if (method == GST_DEINTERLACE_INTERLACED) {
+ const gchar *temp =
+ gst_structure_get_string (gst_caps_get_structure (caps, 0),
+ "interlacing-method");
+ if (temp && g_str_equal (temp, "telecine"))
+ method = GST_DEINTERLACE_TELECINE;
+ }
+
+ return method;
+}
+
+static void
+gst_deinterlace_get_buffer_state (GstDeinterlace * self, GstBuffer * buffer,
+ guint8 * state, GstDeinterlaceInterlacingMethod * i_method)
+{
+ GstDeinterlaceInterlacingMethod interlacing_method;
+
+ if (!(i_method || state))
+ return;
+
+ interlacing_method =
+ gst_deinterlace_get_interlacing_method (GST_BUFFER_CAPS (buffer));
+
+ if (state) {
+ if (interlacing_method == GST_DEINTERLACE_TELECINE) {
+ if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_RFF)) {
+ *state = GST_DEINTERLACE_BUFFER_STATE_DROP;
+ } else if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_ONEFIELD)) {
+ /* tc top if tff, tc bottom otherwise */
+ if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_TFF)) {
+ *state = GST_DEINTERLACE_BUFFER_STATE_TC_T;
+ } else {
+ *state = GST_DEINTERLACE_BUFFER_STATE_TC_B;
+ }
+ } else if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_PROGRESSIVE)) {
+ *state = GST_DEINTERLACE_BUFFER_STATE_TC_P;
+ } else {
+ *state = GST_DEINTERLACE_BUFFER_STATE_TC_M;
+ }
+ } else {
+ if (interlacing_method == GST_DEINTERLACE_INTERLACED) {
+ *state = GST_DEINTERLACE_BUFFER_STATE_I;
+ } else {
+ *state = GST_DEINTERLACE_BUFFER_STATE_P;
+ }
+ }
+ }
+
+ if (i_method)
+ *i_method = interlacing_method;
+}
+
static void
gst_deinterlace_push_history (GstDeinterlace * self, GstBuffer * buffer)
{
GstBuffer *field1, *field2;
guint fields_to_push = (onefield) ? 1 : (!repeated) ? 2 : 3;
gint field1_flags, field2_flags;
+ GstDeinterlaceInterlacingMethod interlacing_method;
+ guint8 buf_state;
g_return_if_fail (self->history_count <
GST_DEINTERLACE_MAX_FIELD_HISTORY - fields_to_push);
- GST_DEBUG_OBJECT (self, "Pushing new buffer to the history: %" GST_TIME_FORMAT
- " with duration %" GST_TIME_FORMAT " and size %u",
+ gst_deinterlace_get_buffer_state (self, buffer, &buf_state,
+ &interlacing_method);
+
+ GST_DEBUG_OBJECT (self,
+ "Pushing new buffer to the history: ptr %p at %" GST_TIME_FORMAT
+ " with duration %" GST_TIME_FORMAT
+ ", size %u, state %u, interlacing method %s", GST_BUFFER_DATA (buffer),
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
- GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)), GST_BUFFER_SIZE (buffer));
+ GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)), GST_BUFFER_SIZE (buffer),
+ buf_state,
+ interlacing_method ==
+ GST_DEINTERLACE_TELECINE ? "TC" : interlacing_method ==
+ GST_DEINTERLACE_INTERLACED ? "I" : "P");
+
+ /* move up for new state */
+ memmove (&self->buf_states[1], &self->buf_states[0],
+ (GST_DEINTERLACE_MAX_BUFFER_STATE_HISTORY - 1) *
+ sizeof (GstDeinterlaceBufferState));
+ self->buf_states[0].state = buf_state;
+ self->buf_states[0].timestamp = GST_BUFFER_TIMESTAMP (buffer);
+ self->buf_states[0].duration = GST_BUFFER_DURATION (buffer);
+ if (self->state_count < GST_DEINTERLACE_MAX_BUFFER_STATE_HISTORY)
+ self->state_count++;
+
+ if (buf_state == GST_DEINTERLACE_BUFFER_STATE_DROP) {
+ GST_DEBUG_OBJECT (self,
+ "Buffer contains only unneeded repeated fields, dropping and not"
+ "adding to field history");
+ gst_buffer_unref (buffer);
+ return;
+ }
+
+ /* telecine does not make use of repeated fields */
+ if (interlacing_method == GST_DEINTERLACE_TELECINE)
+ repeated = FALSE;
for (i = GST_DEINTERLACE_MAX_FIELD_HISTORY - 1; i >= fields_to_push; i--) {
self->field_history[i].buf = self->field_history[i - fields_to_push].buf;
field2_flags = PICTURE_INTERLACED_TOP;
}
- /* Timestamps are assigned to the field buffers under the assumption that
- the timestamp of the buffer equals the first fields timestamp */
+ if (interlacing_method != GST_DEINTERLACE_TELECINE) {
+ /* Timestamps are assigned to the field buffers under the assumption that
+ the timestamp of the buffer equals the first fields timestamp */
- timestamp = GST_BUFFER_TIMESTAMP (buffer);
- GST_BUFFER_TIMESTAMP (field1) = timestamp;
- GST_BUFFER_TIMESTAMP (field2) = timestamp + self->field_duration;
+ timestamp = GST_BUFFER_TIMESTAMP (buffer);
+ GST_BUFFER_TIMESTAMP (field1) = timestamp;
+ GST_BUFFER_TIMESTAMP (field2) = timestamp + self->field_duration;
+ if (repeated)
+ GST_BUFFER_TIMESTAMP (field2) += self->field_duration;
+ }
if (repeated) {
self->field_history[2].buf = field1;
return TRUE;
}
+static gboolean
+gst_deinterlace_fix_timestamps (GstDeinterlace * self,
+ GstDeinterlaceField * field1, GstDeinterlaceField * field2)
+{
+ GstDeinterlaceField *field3, *field4;
+ GstDeinterlaceInterlacingMethod interlacing_method;
+
+ if (self->pattern_lock && self->pattern > -1) {
+ /* accurate pattern-locked timestamp adjustment */
+ if (!self->pattern_count)
+ gst_deinterlace_update_pattern_timestamps (self);
+
+ GST_BUFFER_TIMESTAMP (field1->buf) =
+ self->pattern_base_ts + self->output_count * self->pattern_buf_dur;
+ GST_BUFFER_DURATION (field1->buf) = self->pattern_buf_dur;
+ self->output_count++;
+ } else {
+ /* naive (but low-latency) timestamp adjustment based on subsequent
+ * fields/buffers */
+ if (field2
+ && GST_BUFFER_DATA (field1->buf) != GST_BUFFER_DATA (field2->buf)) {
+ if (GST_BUFFER_TIMESTAMP (field1->buf) +
+ GST_BUFFER_DURATION (field1->buf) ==
+ GST_BUFFER_TIMESTAMP (field2->buf)) {
+ GST_BUFFER_TIMESTAMP (field1->buf) =
+ GST_BUFFER_TIMESTAMP (field2->buf) =
+ (GST_BUFFER_TIMESTAMP (field1->buf) +
+ GST_BUFFER_TIMESTAMP (field2->buf)) / 2;
+ } else {
+ GST_BUFFER_TIMESTAMP (field2->buf) = GST_BUFFER_TIMESTAMP (field1->buf);
+ }
+ }
+
+ if (self->history_count < 3) {
+ GST_DEBUG_OBJECT (self, "Need more fields (have %d, need 3)",
+ self->history_count);
+ return FALSE;
+ }
+
+ field3 = &self->field_history[self->history_count - 3];
+ interlacing_method =
+ gst_deinterlace_get_interlacing_method (GST_BUFFER_CAPS (field3->buf));
+ if (interlacing_method == GST_DEINTERLACE_TELECINE) {
+ if (self->history_count < 4) {
+ GST_DEBUG_OBJECT (self, "Need more fields (have %d, need 4)",
+ self->history_count);
+ return FALSE;
+ }
+
+ field4 = &self->field_history[self->history_count - 4];
+ if (GST_BUFFER_DATA (field3->buf) != GST_BUFFER_DATA (field4->buf)) {
+ /* telecine fields in separate buffers */
+ GST_BUFFER_TIMESTAMP (field3->buf) =
+ (GST_BUFFER_TIMESTAMP (field3->buf) +
+ GST_BUFFER_TIMESTAMP (field4->buf)) / 2;
+ }
+ }
+
+ GST_BUFFER_DURATION (field1->buf) =
+ GST_BUFFER_TIMESTAMP (field3->buf) - GST_BUFFER_TIMESTAMP (field1->buf);
+ }
+
+ GST_DEBUG_OBJECT (self,
+ "Field 1 adjusted to ts %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (field1->buf)),
+ GST_TIME_ARGS (GST_BUFFER_DURATION (field1->buf)));
+ return TRUE;
+}
+
+static void
+gst_deinterlace_get_pattern_lock (GstDeinterlace * self, gboolean * flush_one)
+{
+ /* loop over all possible patterns and all possible phases
+ * giving each a score. the highest score gets the lock */
+ /* the score is calculated as the number of matched buffers in the
+ * sequence starting at the phase offset with those from the history
+ * then the longest duration pattern match is taken. if there is more than
+ * one pattern matching all buffers, we take the longest pattern of those.
+ * matches to complete patterns are preferred. if no non-trivial pattern is
+ * matched, trivial patterns are tested. */
+ gint i, j, k, score, pattern, phase;
+ const gint state_count = self->state_count;
+ const gint n_required = self->ignore_obscure ?
+ GST_DEINTERLACE_OBSCURE_THRESHOLD :
+ GST_DEINTERLACE_MAX_BUFFER_STATE_HISTORY;
+
+ /* set unknown pattern as this is used in logic outside this function */
+ self->pattern = -1;
+
+ /* wait for more buffers */
+ if (!self->have_eos && state_count < n_required) {
+ GST_DEBUG_OBJECT (self, "Need more buffers in state history - %d/%d",
+ state_count, n_required);
+ return;
+ }
+
+ score = pattern = phase = -1;
+
+ /* loop over all patterns */
+ for (i = 0; i < G_N_ELEMENTS (telecine_patterns); i++) {
+ const guint8 length = telecine_patterns[i].length;
+
+ if (self->ignore_obscure && i >= GST_DEINTERLACE_OBSCURE_THRESHOLD)
+ break;
+
+ if (state_count < length)
+ continue;
+
+ /* loop over all phases */
+ for (j = 0; j < length; j++) {
+ /* low-latency mode looks at past buffers, high latency at future buffers */
+ const gint state_idx = (self->low_latency ? length : state_count) - 1;
+ /* loop over history, breaking on differing buffer states */
+ for (k = 0; k < length && k < state_count; k++) {
+ const guint8 hist = self->buf_states[state_idx - k].state;
+ const guint8 patt = telecine_patterns[i].states[(j + k) % length];
+ if (!(hist & patt))
+ break;
+ }
+
+ /* make complete matches more signficant */
+ if (k == length)
+ k += GST_DEINTERLACE_MAX_BUFFER_STATE_HISTORY;
+
+ /* take as new best pattern if the number of matched buffers is more than
+ * for other patterns */
+ if (k > score) {
+ score = k;
+ pattern = i;
+ phase = j;
+ if (self->low_latency) {
+ /* state_idx + 1 is the number of buffers yet to be pushed out
+ * so length - state_idx - 1 is the number of old buffers in the
+ * pattern */
+ phase = (phase + length - state_idx - 1) % length;
+ }
+ }
+ }
+ }
+
+ GST_DEBUG_OBJECT (self,
+ "Final pattern match result: pa %d, ph %d, l %d, s %d", pattern, phase,
+ telecine_patterns[pattern].length, score);
+ self->pattern = pattern;
+ self->pattern_phase = phase;
+ self->pattern_count = 0;
+ self->output_count = 0;
+ self->pattern_lock = TRUE;
+
+ /* check for the case that the first field of the pattern is an orphan */
+ if (pattern > 1
+ && telecine_patterns[pattern].states[phase] & (GST_ONE | GST_INT)) {
+ gint i = phase, field_count = 0;
+ guint8 state = telecine_patterns[pattern].states[i];
+
+ do {
+ if (state & GST_ONE) {
+ field_count++;
+ } else if (!(state & GST_DRP)) {
+ field_count += 2;
+ }
+ i++;
+ i %= telecine_patterns[pattern].length;
+ state = telecine_patterns[pattern].states[i];
+ } while (!(state & GST_PRG));
+
+ /* if field_count is odd, we have an orphan field at the beginning of the
+ * sequence
+ * note - don't do this in low-latency mode as we are somewhere within the
+ * pattern already */
+ if (!self->low_latency && (*flush_one = field_count & 1)) {
+ GST_DEBUG_OBJECT (self, "Orphan field detected at the beginning of the "
+ "pattern - it will be deinterlaced.");
+ }
+ }
+}
+
static GstFlowReturn
gst_deinterlace_output_frame (GstDeinterlace * self, gboolean flushing)
{
GstClockTime timestamp;
- GstFlowReturn ret = GST_FLOW_OK;
- gint fields_required = 0;
- gint cur_field_idx = 0;
+ GstFlowReturn ret;
+ gint fields_required;
+ gint cur_field_idx;
GstBuffer *buf, *outbuf;
+ GstDeinterlaceField *field1, *field2;
+ GstDeinterlaceInterlacingMethod interlacing_method;
+ guint8 buf_state;
+ gboolean hl_no_lock; /* indicates high latency timestamp adjustment but no pattern lock (could be ONEF or I) */
+ gboolean same_buffer; /* are field1 and field2 in the same buffer? */
+ gboolean flush_one; /* used for flushing one field when in high latency mode and not locked */
+ TelecinePattern pattern;
+ guint8 phase, count;
+ const GstDeinterlaceLocking locking = self->locking;
+
+restart:
+ ret = GST_FLOW_OK;
+ fields_required = 0;
+ cur_field_idx = 0;
+ hl_no_lock = FALSE;
+ same_buffer = FALSE;
+ flush_one = FALSE;
+ self->need_more = FALSE;
+ phase = self->pattern_phase;
+ count = self->pattern_count;
+
+ if (!self->history_count) {
+ GST_DEBUG_OBJECT (self, "History is empty, waiting for more buffers!");
+ goto need_more;
+ }
- gst_deinterlace_set_method (self, self->user_set_method_id);
- fields_required = gst_deinterlace_method_get_fields_required (self->method);
-
- if (self->history_count < fields_required) {
- if (flushing) {
- /* FIXME: if there are any methods implemented that output different
- * dimensions (e.g. half height) that require more than one field of
- * history, it is desirable to degrade to something that outputs
- * half-height also */
- gst_deinterlace_set_method (self,
- self->history_count >= 2 ?
- GST_DEINTERLACE_VFIR : GST_DEINTERLACE_LINEAR);
+ field1 = &self->field_history[self->history_count - 1];
+
+ if (locking != GST_DEINTERLACE_LOCKING_NONE) {
+ if (!self->state_count) {
+ GST_ERROR_OBJECT (self,
+ "BROKEN! Fields in history + no states should not happen!");
+ return GST_FLOW_ERROR;
+ }
+
+ gst_deinterlace_get_buffer_state (self, field1->buf, &buf_state,
+ &interlacing_method);
+
+ if (self->pattern != -1)
+ pattern = telecine_patterns[self->pattern];
+
+ /* patterns 0 and 1 are interlaced, the rest are telecine */
+ if (self->pattern > 1)
+ interlacing_method = GST_DEINTERLACE_TELECINE;
+
+ if (self->pattern == -1 || self->pattern_refresh
+ || !(buf_state & pattern.states[(phase + count) % pattern.length])) {
+ /* no pattern, pattern refresh set or unexpected buffer state */
+ self->pattern_lock = FALSE;
+ self->pattern_refresh = TRUE;
+
+ /* refresh pattern lock */
+ gst_deinterlace_get_pattern_lock (self, &flush_one);
+
+ if (self->pattern != -1) {
+ /* locked onto a valid pattern so refresh complete */
+ GST_DEBUG_OBJECT (self, "Pattern locked! %s starting at %d",
+ telecine_patterns[self->pattern].nick, self->pattern_phase);
+ self->pattern_refresh = FALSE;
+ } else if (!self->low_latency) {
+ if (!self->pattern_lock) {
+ goto need_more;
+ } else {
+ hl_no_lock = TRUE;
+ }
+ }
+
+ /* setcaps on sink and src pads */
+ gst_deinterlace_setcaps (self->sinkpad, GST_PAD_CAPS (self->sinkpad));
+
+ if (flush_one && self->drop_orphans) {
+ GST_DEBUG_OBJECT (self, "Dropping orphan first field");
+ gst_buffer_unref (gst_deinterlace_pop_history (self));
+ goto restart;
+ }
+ }
+ } else {
+ gst_deinterlace_get_buffer_state (self, field1->buf, NULL,
+ &interlacing_method);
+ }
+
+ same_buffer = self->history_count >= 2
+ && (GST_BUFFER_DATA (field1->buf) ==
+ GST_BUFFER_DATA (self->field_history[self->history_count - 2].buf));
+
+ if ((flushing && self->history_count == 1) || (flush_one
+ && !self->drop_orphans) || (hl_no_lock && (self->history_count == 1
+ || !same_buffer))) {
+ GST_DEBUG_OBJECT (self, "Flushing one field using linear method");
+ gst_deinterlace_set_method (self, GST_DEINTERLACE_LINEAR);
+ fields_required = gst_deinterlace_method_get_fields_required (self->method);
+ } else if (interlacing_method == GST_DEINTERLACE_TELECINE
+ && (self->low_latency > 0 || self->pattern != -1 || (hl_no_lock
+ && same_buffer
+ && GST_BUFFER_FLAG_IS_SET (field1->buf,
+ GST_VIDEO_BUFFER_PROGRESSIVE)))) {
+ /* telecined - we reconstruct frames by weaving pairs of fields */
+ fields_required = 2;
+ if (!flushing && self->history_count < fields_required) {
+ GST_DEBUG_OBJECT (self, "Need more fields (have %d, need %d)",
+ self->history_count, fields_required);
+ goto need_more;
+ }
+
+ field2 = &self->field_history[self->history_count - 2];
+ if (!gst_deinterlace_fix_timestamps (self, field1, field2) && !flushing)
+ goto need_more;
+
+ if (same_buffer) {
+ /* telecine progressive */
+ GstBuffer *field1_buf;
+
+ GST_DEBUG_OBJECT (self,
+ "Frame type: Telecine Progressive; pushing buffer as a frame");
+ /* pop and push */
+ field1_buf = gst_deinterlace_pop_history (self);
+ /* field2 is the same buffer as field1, but we need to remove it from
+ * the history anyway */
+ gst_buffer_unref (gst_deinterlace_pop_history (self));
+ /* set the caps from the src pad on the buffer as they should be correct */
+ gst_buffer_set_caps (field1_buf, GST_PAD_CAPS (self->srcpad));
+ GST_DEBUG_OBJECT (self,
+ "[OUT] ts %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT ", end %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (field1_buf)),
+ GST_TIME_ARGS (GST_BUFFER_DURATION (field1_buf)),
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (field1_buf) +
+ GST_BUFFER_DURATION (field1_buf)));
+ return gst_pad_push (self->srcpad, field1_buf);
+ } else {
+ /* telecine fields in separate buffers */
+
+ /* check field1 and field2 buffer caps and flags are corresponding */
+ if (field1->flags == field2->flags) {
+ /* ERROR - fields are of same parity - what should be done here?
+ * perhaps deinterlace the tip field and start again? */
+ GST_ERROR_OBJECT (self, "Telecine mixed with fields of same parity!");
+ }
+ GST_DEBUG_OBJECT (self,
+ "Frame type: Telecine Mixed; weaving tip two fields into a frame");
+ /* set method to WEAVE */
+ gst_deinterlace_set_method (self, GST_DEINTERLACE_WEAVE);
+ }
+ } else if (interlacing_method == GST_DEINTERLACE_INTERLACED || (hl_no_lock
+ && interlacing_method == GST_DEINTERLACE_TELECINE && same_buffer
+ && !GST_BUFFER_FLAG_IS_SET (field1->buf,
+ GST_VIDEO_BUFFER_PROGRESSIVE))) {
+ gst_deinterlace_set_method (self, self->user_set_method_id);
+ fields_required = gst_deinterlace_method_get_fields_required (self->method);
+ if (flushing && self->history_count < fields_required) {
+ /* note: we already checked for flushing with history count == 1 above
+ * so we must have 2 or more fields in here */
+ gst_deinterlace_set_method (self, GST_DEINTERLACE_VFIR);
fields_required =
gst_deinterlace_method_get_fields_required (self->method);
GST_DEBUG_OBJECT (self, "Flushing field(s) using %s method",
methods_types[self->method_id].value_nick);
- } else {
- /* Not enough fields in the history */
+ }
+
+ /* Not enough fields in the history */
+ if (self->history_count < fields_required) {
GST_DEBUG_OBJECT (self, "Need more fields (have %d, need %d)",
self->history_count, fields_required);
- return GST_FLOW_OK;
+ goto need_more;
}
- }
- while (self->history_count >= fields_required) {
- if (self->fields == GST_DEINTERLACE_ALL)
- GST_DEBUG_OBJECT (self, "All fields");
- else if (self->fields == GST_DEINTERLACE_TF)
- GST_DEBUG_OBJECT (self, "Top fields");
- else if (self->fields == GST_DEINTERLACE_BF)
- GST_DEBUG_OBJECT (self, "Bottom fields");
-
- cur_field_idx = self->history_count - fields_required;
-
- if ((self->field_history[cur_field_idx].flags == PICTURE_INTERLACED_TOP
- && self->fields == GST_DEINTERLACE_TF) ||
- self->fields == GST_DEINTERLACE_ALL) {
- GST_DEBUG_OBJECT (self, "deinterlacing top field");
-
- /* create new buffer */
- 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;
+ GST_DEBUG_OBJECT (self,
+ "Frame type: Interlaced; deinterlacing using %s method",
+ methods_types[self->method_id].value_nick);
+ } else {
+ GstBuffer *field1_buf;
- 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);
+ /* progressive */
+ fields_required = 2;
- gst_buffer_unref (outbuf);
- outbuf = gst_buffer_try_new_and_alloc (self->frame_size);
+ /* Not enough fields in the history */
+ if (self->history_count < fields_required) {
+ GST_DEBUG_OBJECT (self, "Need more fields (have %d, need %d)",
+ self->history_count, fields_required);
+ goto need_more;
+ }
- if (!outbuf)
- return GST_FLOW_ERROR;
+ field2 = &self->field_history[self->history_count - 2];
+ if (GST_BUFFER_DATA (field1->buf) != GST_BUFFER_DATA (field2->buf)) {
+ /* ERROR - next two fields in field history are not one progressive buffer - weave? */
+ GST_ERROR_OBJECT (self,
+ "Progressive buffer but two fields at tip aren't in the same buffer!");
+ }
- gst_buffer_set_caps (outbuf, GST_PAD_CAPS (self->srcpad));
- }
+ GST_DEBUG_OBJECT (self,
+ "Frame type: Progressive; pushing buffer as a frame");
+ /* pop and push */
+ field1_buf = gst_deinterlace_pop_history (self);
+ /* field2 is the same buffer as field1, but we need to remove it from the
+ * history anyway */
+ gst_buffer_unref (gst_deinterlace_pop_history (self));
+ GST_DEBUG_OBJECT (self,
+ "[OUT] ts %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT ", end %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (field1_buf)),
+ GST_TIME_ARGS (GST_BUFFER_DURATION (field1_buf)),
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (field1_buf) +
+ GST_BUFFER_DURATION (field1_buf)));
+ return gst_pad_push (self->srcpad, field1_buf);
+ }
- g_return_val_if_fail (self->history_count - 1 -
- gst_deinterlace_method_get_latency (self->method) >= 0,
- GST_FLOW_ERROR);
+ if (self->fields == GST_DEINTERLACE_ALL
+ || interlacing_method == GST_DEINTERLACE_TELECINE)
+ GST_DEBUG_OBJECT (self, "All fields");
+ else if (self->fields == GST_DEINTERLACE_TF)
+ GST_DEBUG_OBJECT (self, "Top fields");
+ else if (self->fields == GST_DEINTERLACE_BF)
+ GST_DEBUG_OBJECT (self, "Bottom fields");
+
+ cur_field_idx = self->history_count - fields_required;
+
+ if ((self->field_history[cur_field_idx].flags == PICTURE_INTERLACED_TOP
+ && (self->fields == GST_DEINTERLACE_TF
+ || interlacing_method == GST_DEINTERLACE_TELECINE))
+ || self->fields == GST_DEINTERLACE_ALL) {
+ GST_DEBUG_OBJECT (self, "deinterlacing top field");
+
+ /* create new buffer */
+ 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 (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);
+
+ gst_buffer_unref (outbuf);
+ outbuf = gst_buffer_try_new_and_alloc (self->frame_size);
+
+ if (!outbuf)
+ return GST_FLOW_ERROR;
+
+ gst_buffer_set_caps (outbuf, GST_PAD_CAPS (self->srcpad));
+ }
+
+ g_return_val_if_fail (self->history_count - 1 -
+ gst_deinterlace_method_get_latency (self->method) >= 0, GST_FLOW_ERROR);
- buf =
- self->field_history[self->history_count - 1 -
- gst_deinterlace_method_get_latency (self->method)].buf;
+ buf =
+ self->field_history[self->history_count - 1 -
+ gst_deinterlace_method_get_latency (self->method)].buf;
+
+ if (interlacing_method != GST_DEINTERLACE_TELECINE) {
timestamp = GST_BUFFER_TIMESTAMP (buf);
GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
GST_BUFFER_DURATION (outbuf) = self->field_duration;
else
GST_BUFFER_DURATION (outbuf) = 2 * self->field_duration;
-
- /* Check if we need to drop the frame because of QoS */
- if (!gst_deinterlace_do_qos (self, GST_BUFFER_TIMESTAMP (buf))) {
- gst_buffer_unref (gst_deinterlace_pop_history (self));
- gst_buffer_unref (outbuf);
- outbuf = NULL;
- ret = GST_FLOW_OK;
- } else {
- /* do magic calculus */
- gst_deinterlace_method_deinterlace_frame (self->method,
- self->field_history, self->history_count, outbuf);
-
- gst_buffer_unref (gst_deinterlace_pop_history (self));
-
- if (gst_deinterlace_clip_buffer (self, outbuf)) {
- ret = gst_pad_push (self->srcpad, outbuf);
- } else {
- ret = GST_FLOW_OK;
- gst_buffer_unref (outbuf);
- }
-
- outbuf = NULL;
- if (ret != GST_FLOW_OK)
- return ret;
- }
+ } else {
+ GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
+ GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf);
}
- /* no calculation done: remove excess field */
- else if (self->field_history[cur_field_idx].flags ==
- PICTURE_INTERLACED_TOP && self->fields == GST_DEINTERLACE_BF) {
- GST_DEBUG_OBJECT (self, "Removing unused top field");
+
+ /* Check if we need to drop the frame because of QoS */
+ if (!gst_deinterlace_do_qos (self, GST_BUFFER_TIMESTAMP (buf))) {
gst_buffer_unref (gst_deinterlace_pop_history (self));
- }
+ gst_buffer_unref (outbuf);
+ outbuf = NULL;
+ ret = GST_FLOW_OK;
+ } else {
+ /* do magic calculus */
+ gst_deinterlace_method_deinterlace_frame (self->method,
+ self->field_history, self->history_count, outbuf);
- cur_field_idx = self->history_count - fields_required;
- if (self->history_count < fields_required)
- break;
+ gst_buffer_unref (gst_deinterlace_pop_history (self));
- /* deinterlace bottom_field */
- if ((self->field_history[cur_field_idx].flags == PICTURE_INTERLACED_BOTTOM
- && self->fields == GST_DEINTERLACE_BF) ||
- self->fields == GST_DEINTERLACE_ALL) {
- GST_DEBUG_OBJECT (self, "deinterlacing bottom field");
+ if (gst_deinterlace_clip_buffer (self, outbuf)) {
+ GST_DEBUG_OBJECT (self,
+ "[OUT] ts %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT ", end %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
+ GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)),
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf) +
+ GST_BUFFER_DURATION (outbuf)));
+ ret = gst_pad_push (self->srcpad, outbuf);
+ } else {
+ ret = GST_FLOW_OK;
+ gst_buffer_unref (outbuf);
+ }
- /* create new buffer */
- ret =
- gst_pad_alloc_buffer (self->srcpad, GST_BUFFER_OFFSET_NONE,
- self->frame_size, GST_PAD_CAPS (self->srcpad), &outbuf);
+ outbuf = NULL;
if (ret != GST_FLOW_OK)
return ret;
+ if (interlacing_method == GST_DEINTERLACE_TELECINE
+ && self->method_id == GST_DEINTERLACE_WEAVE) {
+ /* pop off the second field */
+ GST_DEBUG_OBJECT (self, "Removing unused field (count: %d)",
+ self->history_count);
+ gst_buffer_unref (gst_deinterlace_pop_history (self));
+ interlacing_method = GST_DEINTERLACE_INTERLACED;
+ return ret;
+ }
+ }
- 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);
-
- gst_buffer_unref (outbuf);
- outbuf = gst_buffer_try_new_and_alloc (self->frame_size);
+ if (flush_one && !self->drop_orphans) {
+ GST_DEBUG_OBJECT (self, "Orphan field deinterlaced - reconfiguring");
+ goto restart;
+ }
+ }
+ /* no calculation done: remove excess field */
+ else if (self->field_history[cur_field_idx].flags ==
+ PICTURE_INTERLACED_TOP && (self->fields == GST_DEINTERLACE_BF
+ && interlacing_method != GST_DEINTERLACE_TELECINE)) {
+ GST_DEBUG_OBJECT (self, "Removing unused top field");
+ gst_buffer_unref (gst_deinterlace_pop_history (self));
+
+ if (flush_one && !self->drop_orphans) {
+ GST_DEBUG_OBJECT (self, "Orphan field deinterlaced - reconfiguring");
+ goto restart;
+ }
+ }
- if (!outbuf)
- return GST_FLOW_ERROR;
+ cur_field_idx = self->history_count - fields_required;
+ if (self->history_count < fields_required)
+ return ret;
- gst_buffer_set_caps (outbuf, GST_PAD_CAPS (self->srcpad));
- }
+ /* deinterlace bottom_field */
+ if ((self->field_history[cur_field_idx].flags == PICTURE_INTERLACED_BOTTOM
+ && (self->fields == GST_DEINTERLACE_BF
+ || interlacing_method == GST_DEINTERLACE_TELECINE))
+ || self->fields == GST_DEINTERLACE_ALL) {
+ GST_DEBUG_OBJECT (self, "deinterlacing bottom field");
+
+ /* create new buffer */
+ 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 (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);
+
+ gst_buffer_unref (outbuf);
+ outbuf = gst_buffer_try_new_and_alloc (self->frame_size);
+
+ if (!outbuf)
+ return GST_FLOW_ERROR;
+
+ gst_buffer_set_caps (outbuf, GST_PAD_CAPS (self->srcpad));
+ }
- g_return_val_if_fail (self->history_count - 1 -
- gst_deinterlace_method_get_latency (self->method) >= 0,
- GST_FLOW_ERROR);
+ g_return_val_if_fail (self->history_count - 1 -
+ gst_deinterlace_method_get_latency (self->method) >= 0, GST_FLOW_ERROR);
- buf =
- self->field_history[self->history_count - 1 -
- gst_deinterlace_method_get_latency (self->method)].buf;
+ buf =
+ self->field_history[self->history_count - 1 -
+ gst_deinterlace_method_get_latency (self->method)].buf;
+ if (interlacing_method != GST_DEINTERLACE_TELECINE) {
timestamp = GST_BUFFER_TIMESTAMP (buf);
GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
GST_BUFFER_DURATION (outbuf) = self->field_duration;
else
GST_BUFFER_DURATION (outbuf) = 2 * self->field_duration;
+ } else {
+ GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
+ GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf);
+ }
- /* Check if we need to drop the frame because of QoS */
- if (!gst_deinterlace_do_qos (self, GST_BUFFER_TIMESTAMP (buf))) {
- gst_buffer_unref (gst_deinterlace_pop_history (self));
- gst_buffer_unref (outbuf);
- outbuf = NULL;
- ret = GST_FLOW_OK;
- } else {
- /* do magic calculus */
- gst_deinterlace_method_deinterlace_frame (self->method,
- self->field_history, self->history_count, outbuf);
+ /* Check if we need to drop the frame because of QoS */
+ if (!gst_deinterlace_do_qos (self, GST_BUFFER_TIMESTAMP (buf))) {
+ gst_buffer_unref (gst_deinterlace_pop_history (self));
+ gst_buffer_unref (outbuf);
+ outbuf = NULL;
+ ret = GST_FLOW_OK;
+ } else {
+ /* do magic calculus */
+ gst_deinterlace_method_deinterlace_frame (self->method,
+ self->field_history, self->history_count, outbuf);
- gst_buffer_unref (gst_deinterlace_pop_history (self));
+ gst_buffer_unref (gst_deinterlace_pop_history (self));
- if (gst_deinterlace_clip_buffer (self, outbuf)) {
- ret = gst_pad_push (self->srcpad, outbuf);
- } else {
- ret = GST_FLOW_OK;
- gst_buffer_unref (outbuf);
- }
+ if (gst_deinterlace_clip_buffer (self, outbuf)) {
+ GST_DEBUG_OBJECT (self,
+ "[OUT] ts %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT ", end %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
+ GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)),
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf) +
+ GST_BUFFER_DURATION (outbuf)));
+ ret = gst_pad_push (self->srcpad, outbuf);
+ } else {
+ ret = GST_FLOW_OK;
+ gst_buffer_unref (outbuf);
+ }
- outbuf = NULL;
- if (ret != GST_FLOW_OK)
- return ret;
+ outbuf = NULL;
+ if (ret != GST_FLOW_OK)
+ return ret;
+ if (interlacing_method == GST_DEINTERLACE_TELECINE
+ && self->method_id == GST_DEINTERLACE_WEAVE) {
+ /* pop off the second field */
+ GST_DEBUG_OBJECT (self, "Removing unused field (count: %d)",
+ self->history_count);
+ gst_buffer_unref (gst_deinterlace_pop_history (self));
+ interlacing_method = GST_DEINTERLACE_INTERLACED;
+ return ret;
}
}
- /* no calculation done: remove excess field */
- else if (self->field_history[cur_field_idx].flags ==
- PICTURE_INTERLACED_BOTTOM && self->fields == GST_DEINTERLACE_TF) {
- GST_DEBUG_OBJECT (self, "Removing unused bottom field");
- gst_buffer_unref (gst_deinterlace_pop_history (self));
+
+ if (flush_one && !self->drop_orphans) {
+ GST_DEBUG_OBJECT (self, "Orphan field deinterlaced - reconfiguring");
+ goto restart;
}
}
+ /* no calculation done: remove excess field */
+ else if (self->field_history[cur_field_idx].flags ==
+ PICTURE_INTERLACED_BOTTOM && (self->fields == GST_DEINTERLACE_TF
+ && interlacing_method != GST_DEINTERLACE_TELECINE)) {
+ GST_DEBUG_OBJECT (self, "Removing unused bottom field");
+ gst_buffer_unref (gst_deinterlace_pop_history (self));
+
+ if (flush_one && !self->drop_orphans) {
+ GST_DEBUG_OBJECT (self, "Orphan field deinterlaced - reconfiguring");
+ goto restart;
+ }
+ }
+
+ return ret;
+need_more:
+ self->need_more = TRUE;
return ret;
}
+static gboolean
+gst_deinterlace_get_latency (GstDeinterlace * self)
+{
+ if (self->locking == GST_DEINTERLACE_LOCKING_AUTO) {
+ gboolean res;
+ GstQuery *query;
+
+ query = gst_query_new_latency ();
+ if ((res = gst_pad_peer_query (self->sinkpad, query))) {
+ gboolean is_live;
+ /* if upstream is live, we use low-latency passive locking mode
+ * else high-latency active locking mode */
+ gst_query_parse_latency (query, &is_live, NULL, NULL);
+ GST_DEBUG_OBJECT (self, "Latency query indicates stream is %s",
+ is_live ? "live - using passive locking" :
+ "not live - using active locking");
+ gst_query_unref (query);
+ return is_live;
+ } else {
+ /* conservatively use passive locking if the query fails */
+ GST_WARNING_OBJECT (self,
+ "Latency query failed - fall back to using passive locking");
+ gst_query_unref (query);
+ return TRUE;
+ }
+ } else {
+ return self->locking - 2;
+ }
+}
+
static GstFlowReturn
gst_deinterlace_chain (GstPad * pad, GstBuffer * buf)
{
GstDeinterlace *self = GST_DEINTERLACE (GST_PAD_PARENT (pad));
+ GstFlowReturn ret = GST_FLOW_OK;
GST_OBJECT_LOCK (self);
if (self->reconfigure) {
GST_OBJECT_UNLOCK (self);
}
- if (self->still_frame_mode || self->passthrough)
+ GST_DEBUG_OBJECT (self,
+ "[IN] ts %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT ", end %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
+ GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf)));
+
+ if (self->still_frame_mode || self->passthrough) {
+ GST_DEBUG_OBJECT (self,
+ "Frame type: Progressive?; pushing buffer using pass-through");
+ GST_DEBUG_OBJECT (self,
+ "[OUT] ts %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT ", end %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
+ GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf)));
+
return gst_pad_push (self->srcpad, buf);
+ }
if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) {
GST_DEBUG_OBJECT (self, "DISCONT buffer, resetting history");
gst_deinterlace_push_history (self, buf);
buf = NULL;
- return gst_deinterlace_output_frame (self, FALSE);
+ do {
+ ret = gst_deinterlace_output_frame (self, FALSE);
+ } while (!self->need_more && self->history_count > 0 && ret == GST_FLOW_OK);
+
+ return ret;
}
static gint
gboolean res = TRUE;
GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad));
GstCaps *srccaps;
+ GstDeinterlaceInterlacingMethod interlacing_method;
+
+ if (self->locking != GST_DEINTERLACE_LOCKING_NONE) {
+ if (self->low_latency == -1)
+ self->low_latency = gst_deinterlace_get_latency (self);
+
+ if (self->pattern_lock) {
+ /* refresh has been successful - we have a lock now */
+ self->pattern_refresh = FALSE;
+ } else {
+ /* if we were not refreshing (!pattern_refresh) the caps have changed
+ * so we need to refresh and we don't have a lock anymore
+ * otherwise we have pattern_fresh and !pattern_lock anyway */
+ self->pattern_refresh = TRUE;
+ self->pattern_lock = FALSE;
+ }
+ }
res =
gst_video_format_parse_caps (caps, &self->format, &self->width,
gst_deinterlace_update_passthrough (self);
- 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, FALSE))
- goto invalid_caps;
+ interlacing_method = gst_deinterlace_get_interlacing_method (caps);
+ if (self->pattern_lock) {
srccaps = gst_caps_copy (caps);
-
- gst_caps_set_simple (srccaps, "framerate", GST_TYPE_FRACTION, fps_n,
- fps_d, NULL);
+ if (self->pattern != -1
+ && G_UNLIKELY (!gst_util_fraction_multiply (self->fps_n, self->fps_d,
+ telecine_patterns[self->pattern].ratio_n,
+ telecine_patterns[self->pattern].ratio_d, &self->fps_n,
+ &self->fps_d)))
+ GST_ERROR_OBJECT (self,
+ "Multiplying the framerate by the telecine pattern ratio overflowed!");
+ gst_caps_set_simple (srccaps, "framerate", GST_TYPE_FRACTION, self->fps_n,
+ self->fps_d, NULL);
+ } else if (self->low_latency > 0) {
+ if (interlacing_method == GST_DEINTERLACE_TELECINE) {
+ /* for initial buffers of a telecine pattern, until there is a lock we
+ * we output naïvely adjusted timestamps */
+ srccaps = gst_caps_copy (caps);
+ gst_caps_set_simple (srccaps, "framerate", GST_TYPE_FRACTION, 0, 1, NULL);
+ } else 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, FALSE))
+ goto invalid_caps;
+
+ srccaps = gst_caps_copy (caps);
+
+ gst_caps_set_simple (srccaps, "framerate", GST_TYPE_FRACTION, fps_n,
+ fps_d, NULL);
+ } else {
+ srccaps = gst_caps_ref (caps);
+ }
} else {
+ /* in high latency pattern locking mode if we don't have a pattern lock,
+ * the sink pad caps are the best we know */
srccaps = gst_caps_ref (caps);
}
if (self->mode != GST_DEINTERLACE_MODE_DISABLED) {
srccaps = gst_caps_make_writable (srccaps);
+ gst_structure_remove_field (gst_caps_get_structure (srccaps, 0),
+ "interlacing-method");
gst_caps_set_simple (srccaps, "interlaced", G_TYPE_BOOLEAN, FALSE, NULL);
}
- gst_deinterlace_reset_history (self, FALSE);
-
if (!gst_pad_set_caps (self->srcpad, srccaps))
goto caps_not_accepted;
gint64 start, end, base;
gdouble rate, applied_rate;
- gst_event_parse_new_segment_full (event, &is_update, &rate, &applied_rate,
- &fmt, &start, &end, &base);
+ gst_event_parse_new_segment_full (event, &is_update, &rate,
+ &applied_rate, &fmt, &start, &end, &base);
gst_deinterlace_reset_qos (self);
gst_deinterlace_reset_history (self, FALSE);
}
/* fall through */
case GST_EVENT_EOS:
+ self->have_eos = TRUE;
gst_deinterlace_reset_history (self, FALSE);
/* fall through */
typedef struct _GstDeinterlace GstDeinterlace;
typedef struct _GstDeinterlaceClass GstDeinterlaceClass;
-#define GST_DEINTERLACE_MAX_FIELD_HISTORY 10
-
typedef enum
{
GST_DEINTERLACE_TOMSMOCOMP,
GST_DEINTERLACE_MODE_DISABLED
} GstDeinterlaceMode;
+typedef enum
+{
+ GST_DEINTERLACE_LOCKING_NONE,
+ GST_DEINTERLACE_LOCKING_AUTO,
+ GST_DEINTERLACE_LOCKING_ACTIVE,
+ GST_DEINTERLACE_LOCKING_PASSIVE,
+} GstDeinterlaceLocking;
+
+#define GST_DEINTERLACE_MAX_FIELD_HISTORY 10
+#define GST_DEINTERLACE_MAX_BUFFER_STATE_HISTORY 50
+/* check max field history is large enough */
+#if GST_DEINTERLACE_MAX_FIELD_HISTORY < GST_DEINTERLACE_MAX_BUFFER_STATE_HISTORY * 3
+#undef GST_DEINTERLACE_MAX_FIELD_HISTORY
+#define GST_DEINTERLACE_MAX_FIELD_HISTORY (GST_DEINTERLACE_MAX_BUFFER_STATE_HISTORY * 3)
+#endif
+
+typedef struct _TelecinePattern TelecinePattern;
+struct _TelecinePattern
+{
+ const gchar *nick;
+ guint8 length;
+ guint8 ratio_n, ratio_d;
+ guint8 states[GST_DEINTERLACE_MAX_BUFFER_STATE_HISTORY];
+};
+
+typedef struct _GstDeinterlaceBufferState GstDeinterlaceBufferState;
+struct _GstDeinterlaceBufferState
+{
+ GstClockTime timestamp;
+ GstClockTime duration;
+ guint8 state;
+};
+
struct _GstDeinterlace
{
GstElement parent;
GstDeinterlaceFields fields;
- GstDeinterlaceMethods method_id; /* current state (differs when flushing) */
- GstDeinterlaceMethods user_set_method_id; /* property value */
+ /* current state (differs when flushing/inverse telecine using weave) */
+ GstDeinterlaceMethods method_id;
+ /* property value */
+ GstDeinterlaceMethods user_set_method_id;
GstDeinterlaceMethod *method;
GstVideoFormat format;
gboolean reconfigure;
GstDeinterlaceMode new_mode;
GstDeinterlaceFields new_fields;
+
+ GstDeinterlaceLocking locking;
+ gint low_latency;
+ gboolean drop_orphans;
+ gboolean ignore_obscure;
+ gboolean pattern_lock;
+ gboolean pattern_refresh;
+ GstDeinterlaceBufferState buf_states[GST_DEINTERLACE_MAX_BUFFER_STATE_HISTORY];
+ gint state_count;
+ gint pattern;
+ guint8 pattern_phase;
+ guint8 pattern_count;
+ guint8 output_count;
+ GstClockTime pattern_base_ts;
+ GstClockTime pattern_buf_dur;
+
+ gboolean need_more;
+ gboolean have_eos;
};
struct _GstDeinterlaceClass
("We got less than expected (received %u, wanted %u, offset %"
G_GUINT64_FORMAT ")",
GST_BUFFER_SIZE (moov), (guint) length, cur_offset));
+ gst_buffer_unref (moov);
ret = GST_FLOW_ERROR;
goto beach;
}
case GST_MAKE_FOURCC ('V', 'P', '8', '0'):
_codec ("On2 VP8");
caps = gst_caps_from_string ("video/x-vp8");
+ break;
case FOURCC_ovc1:
_codec ("VC-1");
caps = gst_caps_new_simple ("video/x-wmv",
matroska-parse.c \
matroska-ids.c \
matroska-mux.c \
+ matroska-read-common.c \
webm-mux.c \
lzo.c
matroska-parse.h \
matroska-ids.h \
matroska-mux.h \
+ matroska-read-common.h \
webm-mux.h \
lzo.h
* (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
* (c) 2006 Tim-Philipp Müller <tim centricular net>
* (c) 2008 Sebastian Dröge <slomo@circular-chaos.org>
+ * (c) 2011 Debarshi Ray <rishi@gnu.org>
*
* matroska-demux.c: matroska file/stream demuxer
*
#include <gst/base/gsttypefindhelper.h>
-#ifdef HAVE_ZLIB
-#include <zlib.h>
-#endif
-
-#ifdef HAVE_BZ2
-#include <bzlib.h>
-#endif
-
#include <gst/pbutils/pbutils.h>
-#include "lzo.h"
-
#include "matroska-demux.h"
#include "matroska-ids.h"
{
GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (object);
- if (demux->src) {
- g_ptr_array_free (demux->src, TRUE);
- demux->src = NULL;
+ if (demux->common.src) {
+ g_ptr_array_free (demux->common.src, TRUE);
+ demux->common.src = NULL;
}
if (demux->global_tags) {
gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
/* initial stream no. */
- demux->src = NULL;
+ demux->common.src = NULL;
demux->writing_app = NULL;
demux->muxing_app = NULL;
- demux->index = NULL;
+ demux->common.index = NULL;
demux->global_tags = NULL;
demux->adapter = gst_adapter_new ();
goto done;
/* only return NOT_LINKED if all other pads returned NOT_LINKED */
- g_assert (demux->src->len == demux->num_streams);
- for (i = 0; i < demux->src->len; i++) {
- GstMatroskaTrackContext *ostream = g_ptr_array_index (demux->src, i);
+ g_assert (demux->common.src->len == demux->common.num_streams);
+ for (i = 0; i < demux->common.src->len; i++) {
+ GstMatroskaTrackContext *ostream = g_ptr_array_index (demux->common.src,
+ i);
if (ostream == NULL)
continue;
GST_DEBUG_OBJECT (demux, "Resetting state");
/* reset input */
- demux->state = GST_MATROSKA_DEMUX_STATE_START;
+ demux->common.state = GST_MATROSKA_READ_STATE_START;
/* clean up existing streams */
- if (demux->src) {
- g_assert (demux->src->len == demux->num_streams);
- for (i = 0; i < demux->src->len; i++) {
- GstMatroskaTrackContext *context = g_ptr_array_index (demux->src, i);
+ if (demux->common.src) {
+ g_assert (demux->common.src->len == demux->common.num_streams);
+ for (i = 0; i < demux->common.src->len; i++) {
+ GstMatroskaTrackContext *context = g_ptr_array_index (demux->common.src,
+ i);
if (context->pad != NULL)
gst_element_remove_pad (GST_ELEMENT (demux), context->pad);
gst_caps_replace (&context->caps, NULL);
gst_matroska_track_free (context);
}
- g_ptr_array_free (demux->src, TRUE);
+ g_ptr_array_free (demux->common.src, TRUE);
}
- demux->src = g_ptr_array_new ();
+ demux->common.src = g_ptr_array_new ();
- demux->num_streams = 0;
+ demux->common.num_streams = 0;
demux->num_a_streams = 0;
demux->num_t_streams = 0;
demux->num_v_streams = 0;
demux->muxing_app = NULL;
/* reset indexes */
- if (demux->index) {
- g_array_free (demux->index, TRUE);
- demux->index = NULL;
+ if (demux->common.index) {
+ g_array_free (demux->common.index, TRUE);
+ demux->common.index = NULL;
}
if (demux->clusters) {
/* reset timers */
demux->clock = NULL;
- demux->time_scale = 1000000;
+ demux->common.time_scale = 1000000;
demux->created = G_MININT64;
- demux->index_parsed = FALSE;
+ demux->common.index_parsed = FALSE;
demux->tracks_parsed = FALSE;
demux->segmentinfo_parsed = FALSE;
demux->attachments_parsed = FALSE;
demux->new_segment = NULL;
}
- if (demux->element_index) {
- gst_object_unref (demux->element_index);
- demux->element_index = NULL;
+ if (demux->common.element_index) {
+ gst_object_unref (demux->common.element_index);
+ demux->common.element_index = NULL;
}
- demux->element_index_writer_id = -1;
+ demux->common.element_index_writer_id = -1;
if (demux->global_tags) {
gst_tag_list_free (demux->global_tags);
}
static gint
-gst_matroska_demux_stream_from_num (GstMatroskaDemux * demux, guint track_num)
-{
- guint n;
-
- g_assert (demux->src->len == demux->num_streams);
- for (n = 0; n < demux->src->len; n++) {
- GstMatroskaTrackContext *context = g_ptr_array_index (demux->src, n);
-
- if (context->num == track_num) {
- return n;
- }
- }
-
- if (n == demux->num_streams)
- GST_WARNING_OBJECT (demux,
- "Failed to find corresponding pad for tracknum %d", track_num);
-
- return -1;
-}
-
-static gint
gst_matroska_demux_encoding_cmp (GstMatroskaTrackEncoding * a,
GstMatroskaTrackEncoding * b)
{
}
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)
{
}
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);
- 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;
-
- 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_demux_read_track_encodings (GstMatroskaDemux * demux,
GstEbmlRead * ebml, GstMatroskaTrackContext * context)
{
{
gint i;
- g_assert (demux->src->len == demux->num_streams);
- for (i = 0; i < demux->src->len; i++) {
- GstMatroskaTrackContext *context = g_ptr_array_index (demux->src, i);
+ g_assert (demux->common.src->len == demux->common.num_streams);
+ for (i = 0; i < demux->common.src->len; i++) {
+ GstMatroskaTrackContext *context = g_ptr_array_index (demux->common.src,
+ i);
if (context->num == num)
return FALSE;
/* allocate generic... if we know the type, we'll g_renew()
* with the precise type */
context = g_new0 (GstMatroskaTrackContext, 1);
- g_ptr_array_add (demux->src, context);
- context->index = demux->num_streams;
+ g_ptr_array_add (demux->common.src, context);
+ context->index = demux->common.num_streams;
context->index_writer_id = -1;
context->type = 0; /* no type yet */
context->default_duration = 0;
GST_MATROSKA_TRACK_LACING;
context->last_flow = GST_FLOW_OK;
context->to_offset = G_MAXINT64;
- demux->num_streams++;
- g_assert (demux->src->len == demux->num_streams);
+ demux->common.num_streams++;
+ g_assert (demux->common.src->len == demux->common.num_streams);
GST_DEBUG_OBJECT (demux, "Stream number %d", context->index);
context->type = 0;
break;
}
- g_ptr_array_index (demux->src, demux->num_streams - 1) = context;
+ g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
+ = context;
break;
}
break;
}
videocontext = (GstMatroskaTrackVideoContext *) context;
- g_ptr_array_index (demux->src, demux->num_streams - 1) = context;
+ g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
+ = context;
while (ret == GST_FLOW_OK &&
gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
break;
audiocontext = (GstMatroskaTrackAudioContext *) context;
- g_ptr_array_index (demux->src, demux->num_streams - 1) = context;
+ g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
+ = context;
while (ret == GST_FLOW_OK &&
gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
if (ret == GST_FLOW_OK || ret == GST_FLOW_UNEXPECTED)
GST_WARNING_OBJECT (ebml, "Unknown stream/codec in track entry header");
- demux->num_streams--;
- g_ptr_array_remove_index (demux->src, demux->num_streams);
- g_assert (demux->src->len == demux->num_streams);
+ demux->common.num_streams--;
+ g_ptr_array_remove_index (demux->common.src, demux->common.num_streams);
+ g_assert (demux->common.src->len == demux->common.num_streams);
if (context) {
gst_matroska_track_free (context);
}
GstMatroskaIndex *entry = NULL;
GArray *index;
- if (!demux->index || !demux->index->len)
+ if (!demux->common.index || !demux->common.index->len)
return NULL;
/* find entry just before or at the requested position */
if (track && track->index_table)
index = track->index_table;
else
- index = demux->index;
+ index = demux->common.index;
entry =
gst_util_array_binary_search (index->data, index->len,
is_newsegment = (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT);
- g_assert (demux->src->len == demux->num_streams);
- for (i = 0; i < demux->src->len; i++) {
+ g_assert (demux->common.src->len == demux->common.num_streams);
+ for (i = 0; i < demux->common.src->len; i++) {
GstMatroskaTrackContext *stream;
- stream = g_ptr_array_index (demux->src, i);
+ stream = g_ptr_array_index (demux->common.src, i);
gst_event_ref (event);
gst_pad_push_event (stream->pad, event);
ret = TRUE;
if (track && track->type == GST_MATROSKA_TRACK_TYPE_VIDEO)
return track;
- for (i = 0; i < demux->src->len; i++) {
+ for (i = 0; i < demux->common.src->len; i++) {
GstMatroskaTrackContext *stream;
- stream = g_ptr_array_index (demux->src, i);
+ stream = g_ptr_array_index (demux->common.src, i);
if (stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO && stream->index_table)
track = stream;
}
GST_DEBUG_OBJECT (demux, "resetting stream state");
- g_assert (demux->src->len == demux->num_streams);
- for (i = 0; i < demux->src->len; i++) {
- GstMatroskaTrackContext *context = g_ptr_array_index (demux->src, i);
+ g_assert (demux->common.src->len == demux->common.num_streams);
+ for (i = 0; i < demux->common.src->len; i++) {
+ GstMatroskaTrackContext *context = g_ptr_array_index (demux->common.src,
+ i);
context->pos = time;
context->set_discont = TRUE;
context->eos = FALSE;
/* seek (relative to matroska segment) */
/* position might be invalid; will error when streaming resumes ... */
- demux->offset = entry->pos + demux->ebml_segment_start;
+ demux->offset = entry->pos + demux->common.ebml_segment_start;
GST_DEBUG_OBJECT (demux, "Seeked to offset %" G_GUINT64_FORMAT ", block %d, "
- "time %" GST_TIME_FORMAT, entry->pos + demux->ebml_segment_start,
+ "time %" GST_TIME_FORMAT, entry->pos + demux->common.ebml_segment_start,
entry->block, GST_TIME_ARGS (entry->time));
/* update the time */
demux->seek_first = TRUE;
demux->last_stop_end = GST_CLOCK_TIME_NONE;
- for (i = 0; i < demux->src->len; i++) {
- GstMatroskaTrackContext *stream = g_ptr_array_index (demux->src, i);
+ for (i = 0; i < demux->common.src->len; i++) {
+ GstMatroskaTrackContext *stream = g_ptr_array_index (demux->common.src, i);
if (reset) {
stream->to_offset = G_MAXINT64;
gst_matroska_demux_search_pos (GstMatroskaDemux * demux, GstClockTime time)
{
GstMatroskaIndex *entry = NULL;
- GstMatroskaDemuxState current_state;
+ GstMatroskaReadState current_state;
GstClockTime otime, prev_cluster_time, current_cluster_time, cluster_time;
gint64 opos, newpos, startpos = 0, current_offset;
gint64 prev_cluster_offset = -1, current_cluster_offset, cluster_offset;
prev_cluster_time = GST_CLOCK_TIME_NONE;
/* store some current state */
- current_state = demux->state;
- g_return_val_if_fail (current_state == GST_MATROSKA_DEMUX_STATE_DATA, NULL);
+ current_state = demux->common.state;
+ g_return_val_if_fail (current_state == GST_MATROSKA_READ_STATE_DATA, NULL);
current_cluster_offset = demux->cluster_offset;
current_cluster_time = demux->cluster_time;
current_offset = demux->offset;
- demux->state = GST_MATROSKA_DEMUX_STATE_SCANNING;
+ demux->common.state = GST_MATROSKA_READ_STATE_SCANNING;
/* estimate using start and current position */
GST_OBJECT_LOCK (demux);
- opos = demux->offset - demux->ebml_segment_start;
+ opos = demux->offset - demux->common.ebml_segment_start;
otime = demux->segment.last_stop;
GST_OBJECT_UNLOCK (demux);
newpos = 0;
/* favour undershoot */
newpos = newpos * 90 / 100;
- newpos += demux->ebml_segment_start;
+ newpos += demux->common.ebml_segment_start;
GST_DEBUG_OBJECT (demux,
"estimated offset for %" GST_TIME_FORMAT ": %" G_GINT64_FORMAT,
}
if (demux->cluster_time != GST_CLOCK_TIME_NONE &&
cluster_time == GST_CLOCK_TIME_NONE) {
- cluster_time = demux->cluster_time * demux->time_scale;
+ cluster_time = demux->cluster_time * demux->common.time_scale;
cluster_offset = demux->cluster_offset;
GST_DEBUG_OBJECT (demux, "found cluster at offset %" G_GINT64_FORMAT
" with time %" GST_TIME_FORMAT, cluster_offset,
entry = g_new0 (GstMatroskaIndex, 1);
entry->time = prev_cluster_time;
- entry->pos = prev_cluster_offset - demux->ebml_segment_start;
+ entry->pos = prev_cluster_offset - demux->common.ebml_segment_start;
GST_DEBUG_OBJECT (demux, "simulated index entry; time %" GST_TIME_FORMAT
", pos %" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->time), entry->pos);
demux->cluster_offset = current_cluster_offset;
demux->cluster_time = current_cluster_time;
demux->offset = current_offset;
- demux->state = current_state;
+ demux->common.state = current_state;
return entry;
}
seeksegment.last_stop, &demux->seek_index, &demux->seek_entry)) ==
NULL) {
/* pull mode without index can scan later on */
- if (demux->index || demux->streaming) {
+ if (demux->common.index || demux->streaming) {
GST_DEBUG_OBJECT (demux, "No matching seek entry in index");
GST_OBJECT_UNLOCK (demux);
return FALSE;
/* upstream takes care of flushing and all that
* ... and newsegment event handling takes care of the rest */
return perform_seek_to_offset (demux,
- entry->pos + demux->ebml_segment_start);
+ entry->pos + demux->common.ebml_segment_start);
}
flush = !!(flags & GST_SEEK_FLAG_FLUSH);
GST_PAD_STREAM_LOCK (demux->sinkpad);
/* pull mode without index can do some scanning */
- if (!demux->streaming && !demux->index) {
+ if (!demux->streaming && !demux->common.index) {
/* need to stop flushing upstream as we need it next */
if (flush)
gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop ());
}
/* check for having parsed index already */
- if (!demux->index_parsed) {
+ if (!demux->common.index_parsed) {
gboolean building_index;
guint64 offset = 0;
GST_OBJECT_LOCK (demux);
/* handle the seek event in the chain function */
- demux->state = GST_MATROSKA_DEMUX_STATE_SEEK;
+ demux->common.state = GST_MATROSKA_READ_STATE_SEEK;
/* no more seek can be issued until state reset to _DATA */
/* copy the event */
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
/* no seeking until we are (safely) ready */
- if (demux->state != GST_MATROSKA_DEMUX_STATE_DATA) {
+ if (demux->common.state != GST_MATROSKA_READ_STATE_DATA) {
GST_DEBUG_OBJECT (demux, "not ready for seeking yet");
return FALSE;
}
goto exit;
}
- for (i = 0; i < demux->src->len; i++) {
- GstMatroskaTrackContext *stream = g_ptr_array_index (demux->src, i);
+ for (i = 0; i < demux->common.src->len; i++) {
+ GstMatroskaTrackContext *stream = g_ptr_array_index (demux->common.src, i);
GST_DEBUG_OBJECT (demux, "segment start %" GST_TIME_FORMAT
", stream %d at %" GST_TIME_FORMAT,
return ret;
}
-/* skip unknown or alike element */
-static GstFlowReturn
-gst_matroska_demux_parse_skip (GstMatroskaDemux * demux, GstEbmlRead * ebml,
- const gchar * parent_name, guint id)
-{
- if (id == GST_EBML_ID_VOID) {
- GST_DEBUG_OBJECT (demux, "Skipping EBML Void element");
- } else if (id == GST_EBML_ID_CRC32) {
- GST_DEBUG_OBJECT (demux, "Skipping EBML CRC32 element");
- } else {
- GST_WARNING_OBJECT (demux,
- "Unknown %s subelement 0x%x - ignoring", parent_name, id);
- }
-
- return gst_ebml_read_skip (ebml);
-}
-
static GstFlowReturn
gst_matroska_demux_parse_header (GstMatroskaDemux * demux, GstEbmlRead * ebml)
{
}
default:
- ret = gst_matroska_demux_parse_skip (demux, ebml, "EBML header", id);
+ ret = gst_matroska_read_common_parse_skip (&demux->common, ebml,
+ "EBML header", id);
if (ret != GST_FLOW_OK)
return ret;
break;
break;
default:
- ret = gst_matroska_demux_parse_skip (demux, ebml, "Track", id);
+ ret = gst_matroska_read_common_parse_skip (&demux->common, ebml,
+ "Track", id);
break;
}
}
}
static GstFlowReturn
-gst_matroska_demux_parse_index_cuetrack (GstMatroskaDemux * demux,
- 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 (demux, ebml, "CueTrackPositions");
-
- if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
- DEBUG_ELEMENT_STOP (demux, 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 (demux, "Invalid CueTrack 0");
- break;
- }
-
- GST_DEBUG_OBJECT (demux, "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 (demux, "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 (demux, "Invalid CueBlockNumber 0");
- break;
- }
-
- GST_DEBUG_OBJECT (demux, "CueBlockNumber: %" G_GUINT64_FORMAT, num);
- idx.block = num;
-
- /* mild sanity check, disregard strange cases ... */
- if (idx.block > G_MAXUINT16) {
- GST_DEBUG_OBJECT (demux, "... looks suspicious, ignoring");
- idx.block = 1;
- }
- break;
- }
-
- default:
- ret = gst_matroska_demux_parse_skip (demux, ebml, "CueTrackPositions",
- id);
- break;
-
- case GST_MATROSKA_ID_CUECODECSTATE:
- case GST_MATROSKA_ID_CUEREFERENCE:
- ret = gst_ebml_read_skip (ebml);
- break;
- }
- }
-
- DEBUG_ELEMENT_STOP (demux, ebml, "CueTrackPositions", ret);
-
- if ((ret == GST_FLOW_OK || ret == GST_FLOW_UNEXPECTED)
- && idx.pos != (guint64) - 1 && idx.track > 0) {
- g_array_append_val (demux->index, idx);
- (*nentries)++;
- } else if (ret == GST_FLOW_OK || ret == GST_FLOW_UNEXPECTED) {
- GST_DEBUG_OBJECT (demux, "CueTrackPositions without valid content");
- }
-
- return ret;
-}
-
-static GstFlowReturn
-gst_matroska_demux_parse_index_pointentry (GstMatroskaDemux * demux,
- GstEbmlRead * ebml)
-{
- guint32 id;
- GstFlowReturn ret;
- GstClockTime time = GST_CLOCK_TIME_NONE;
- guint nentries = 0;
-
- DEBUG_ELEMENT_START (demux, ebml, "CuePoint");
-
- if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
- DEBUG_ELEMENT_STOP (demux, 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 (demux, "CueTime: %" G_GUINT64_FORMAT, time);
- time = time * demux->time_scale;
- break;
- }
-
- /* position in the file + track to which it belongs */
- case GST_MATROSKA_ID_CUETRACKPOSITIONS:
- {
- if ((ret =
- gst_matroska_demux_parse_index_cuetrack (demux, ebml,
- &nentries)) != GST_FLOW_OK)
- break;
- break;
- }
-
- default:
- ret = gst_matroska_demux_parse_skip (demux, ebml, "CuePoint", id);
- break;
- }
- }
-
- DEBUG_ELEMENT_STOP (demux, ebml, "CuePoint", ret);
-
- if (nentries > 0) {
- if (time == GST_CLOCK_TIME_NONE) {
- GST_WARNING_OBJECT (demux, "CuePoint without valid time");
- g_array_remove_range (demux->index, demux->index->len - nentries,
- nentries);
- } else {
- gint i;
-
- for (i = demux->index->len - nentries; i < demux->index->len; i++) {
- GstMatroskaIndex *idx =
- &g_array_index (demux->index, GstMatroskaIndex, i);
-
- idx->time = time;
- GST_DEBUG_OBJECT (demux, "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 (demux, "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_demux_parse_index (GstMatroskaDemux * demux, GstEbmlRead * ebml)
-{
- guint32 id;
- GstFlowReturn ret = GST_FLOW_OK;
- guint i;
-
- if (demux->index)
- g_array_free (demux->index, TRUE);
- demux->index =
- g_array_sized_new (FALSE, FALSE, sizeof (GstMatroskaIndex), 128);
-
- DEBUG_ELEMENT_START (demux, ebml, "Cues");
-
- if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
- DEBUG_ELEMENT_STOP (demux, 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_demux_parse_index_pointentry (demux, ebml);
- break;
-
- default:
- ret = gst_matroska_demux_parse_skip (demux, ebml, "Cues", id);
- break;
- }
- }
- DEBUG_ELEMENT_STOP (demux, ebml, "Cues", ret);
-
- /* Sort index by time, smallest time first, for easier searching */
- g_array_sort (demux->index, (GCompareFunc) gst_matroska_index_compare);
-
- /* Now sort the track specific index entries into their own arrays */
- for (i = 0; i < demux->index->len; i++) {
- GstMatroskaIndex *idx = &g_array_index (demux->index, GstMatroskaIndex, i);
- gint track_num;
- GstMatroskaTrackContext *ctx;
-
- if (demux->element_index) {
- gint writer_id;
-
- if (idx->track != 0 &&
- (track_num =
- gst_matroska_demux_stream_from_num (demux, idx->track)) != -1) {
- ctx = g_ptr_array_index (demux->src, track_num);
-
- if (ctx->index_writer_id == -1)
- gst_index_get_writer_id (demux->element_index, GST_OBJECT (ctx->pad),
- &ctx->index_writer_id);
- writer_id = ctx->index_writer_id;
- } else {
- if (demux->element_index_writer_id == -1)
- gst_index_get_writer_id (demux->element_index, GST_OBJECT (demux),
- &demux->element_index_writer_id);
- writer_id = demux->element_index_writer_id;
- }
-
- GST_LOG_OBJECT (demux, "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 (demux->element_index, writer_id,
- GST_ASSOCIATION_FLAG_KEY_UNIT, GST_FORMAT_TIME, idx->time,
- GST_FORMAT_BYTES, idx->pos + demux->ebml_segment_start, NULL);
- }
-
- if (idx->track == 0)
- continue;
-
- track_num = gst_matroska_demux_stream_from_num (demux, idx->track);
- if (track_num == -1)
- continue;
-
- ctx = g_ptr_array_index (demux->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);
- }
-
- demux->index_parsed = TRUE;
-
- /* sanity check; empty index normalizes to no index */
- if (demux->index->len == 0) {
- g_array_free (demux->index, TRUE);
- demux->index = NULL;
- }
-
- return ret;
-}
-
-static GstFlowReturn
gst_matroska_demux_parse_info (GstMatroskaDemux * demux, GstEbmlRead * ebml)
{
GstFlowReturn ret = GST_FLOW_OK;
GST_DEBUG_OBJECT (demux, "TimeCodeScale: %" G_GUINT64_FORMAT, num);
- demux->time_scale = num;
+ demux->common.time_scale = num;
break;
}
}
default:
- ret = gst_matroska_demux_parse_skip (demux, ebml, "SegmentInfo", id);
+ ret = gst_matroska_read_common_parse_skip (&demux->common, ebml,
+ "SegmentInfo", id);
break;
/* fall through */
GstClockTime dur_u;
dur_u = gst_gdouble_to_guint64 (dur_f *
- gst_guint64_to_gdouble (demux->time_scale));
+ gst_guint64_to_gdouble (demux->common.time_scale));
if (GST_CLOCK_TIME_IS_VALID (dur_u) && dur_u <= G_MAXINT64)
gst_segment_set_duration (&demux->segment, GST_FORMAT_TIME, dur_u);
}
break;
default:
- ret = gst_matroska_demux_parse_skip (demux, ebml, "SimpleTag", id);
+ ret = gst_matroska_read_common_parse_skip (&demux->common, ebml,
+ "SimpleTag", id);
break;
/* fall-through */
break;
default:
- ret = gst_matroska_demux_parse_skip (demux, ebml, "Tag", id);
+ ret = gst_matroska_read_common_parse_skip (&demux->common, ebml,
+ "Tag", id);
break;
}
}
break;
default:
- ret = gst_matroska_demux_parse_skip (demux, ebml, "Tags", id);
+ ret = gst_matroska_read_common_parse_skip (&demux->common, ebml,
+ "Tags", id);
break;
/* FIXME: Use to limit the tags to specific pads */
case GST_MATROSKA_ID_TARGETS:
break;
default:
- ret = gst_matroska_demux_parse_skip (demux, ebml, "AttachedFile", id);
+ ret = gst_matroska_read_common_parse_skip (&demux->common, ebml,
+ "AttachedFile", id);
break;
case GST_MATROSKA_ID_FILEUID:
ret = gst_ebml_read_skip (ebml);
break;
default:
- ret = gst_matroska_demux_parse_skip (demux, ebml, "Attachments", id);
+ ret = gst_matroska_read_common_parse_skip (&demux->common, ebml,
+ "Attachments", id);
break;
}
}
GST_LOG_OBJECT (demux, "Sync to %" GST_TIME_FORMAT,
GST_TIME_ARGS (demux->segment.last_stop));
- g_assert (demux->num_streams == demux->src->len);
- for (stream_nr = 0; stream_nr < demux->src->len; stream_nr++) {
+ g_assert (demux->common.num_streams == demux->common.src->len);
+ for (stream_nr = 0; stream_nr < demux->common.src->len; stream_nr++) {
GstMatroskaTrackContext *context;
- context = g_ptr_array_index (demux->src, stream_nr);
+ context = g_ptr_array_index (demux->common.src, stream_nr);
GST_LOG_OBJECT (demux,
"Checking for resync on stream %d (%" GST_TIME_FORMAT ")", stream_nr,
size -= n;
/* fetch stream from num */
- stream_num = gst_matroska_demux_stream_from_num (demux, num);
+ stream_num = gst_matroska_read_common_stream_from_num (&demux->common,
+ num);
if (G_UNLIKELY (size < 3)) {
GST_WARNING_OBJECT (demux, "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 >= demux->num_streams)) {
+ stream_num >= demux->common.num_streams)) {
/* let's not give up on a stray invalid track number */
GST_WARNING_OBJECT (demux,
"Invalid stream %d for track number %" G_GUINT64_FORMAT
goto done;
}
- stream = g_ptr_array_index (demux->src, stream_num);
+ stream = g_ptr_array_index (demux->common.src, stream_num);
/* time (relative to cluster time) */
time = ((gint16) GST_READ_UINT16_BE (data));
}
default:
- ret = gst_matroska_demux_parse_skip (demux, ebml, "BlockGroup", id);
+ ret = gst_matroska_read_common_parse_skip (&demux->common, ebml,
+ "BlockGroup", id);
break;
case GST_MATROSKA_ID_BLOCKVIRTUAL:
gint64 lace_time = 0;
gboolean delta_unit;
- stream = g_ptr_array_index (demux->src, stream_num);
+ stream = g_ptr_array_index (demux->common.src, stream_num);
if (cluster_time != GST_CLOCK_TIME_NONE) {
/* FIXME: What to do with negative timestamps? Give timestamp 0 or -1?
lace_time = 0;
} else {
if (stream->timecodescale == 1.0)
- lace_time = (cluster_time + time) * demux->time_scale;
+ lace_time = (cluster_time + time) * demux->common.time_scale;
else
lace_time =
gst_util_guint64_to_gdouble ((cluster_time + time) *
- demux->time_scale) * stream->timecodescale;
+ demux->common.time_scale) * stream->timecodescale;
}
} else {
lace_time = GST_CLOCK_TIME_NONE;
if (block_duration) {
if (stream->timecodescale == 1.0)
- duration = gst_util_uint64_scale (block_duration, demux->time_scale, 1);
+ duration = gst_util_uint64_scale (block_duration,
+ demux->common.time_scale, 1);
else
duration =
gst_util_gdouble_to_guint64 (gst_util_guint64_to_gdouble
- (gst_util_uint64_scale (block_duration, demux->time_scale,
+ (gst_util_uint64_scale (block_duration, demux->common.time_scale,
1)) * stream->timecodescale);
} else if (stream->default_duration) {
duration = stream->default_duration * laces;
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (sub)),
GST_TIME_ARGS (GST_BUFFER_DURATION (sub)));
- if (demux->element_index) {
+ if (demux->common.element_index) {
if (stream->index_writer_id == -1)
- gst_index_get_writer_id (demux->element_index,
+ gst_index_get_writer_id (demux->common.element_index,
GST_OBJECT (stream->pad), &stream->index_writer_id);
GST_LOG_OBJECT (demux, "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 (demux->element_index,
+ gst_index_add_association (demux->common.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,
}
default:
- ret = gst_matroska_demux_parse_skip (demux, ebml, "SeekHead", id);
+ ret = gst_matroska_read_common_parse_skip (&demux->common, ebml,
+ "SeekHead", id);
break;
}
}
}
/* check for validity */
- if (seek_pos + demux->ebml_segment_start + 12 >= length) {
+ if (seek_pos + demux->common.ebml_segment_start + 12 >= length) {
GST_WARNING_OBJECT (demux,
"SeekHead reference lies outside file!" " (%"
G_GUINT64_FORMAT "+%" G_GUINT64_FORMAT "+12 >= %"
- G_GUINT64_FORMAT ")", seek_pos, demux->ebml_segment_start, length);
+ G_GUINT64_FORMAT ")", seek_pos, demux->common.ebml_segment_start,
+ length);
break;
}
/* only pick up index location when streaming */
if (demux->streaming) {
if (seek_id == GST_MATROSKA_ID_CUES) {
- demux->index_offset = seek_pos + demux->ebml_segment_start;
+ demux->index_offset = seek_pos + demux->common.ebml_segment_start;
GST_DEBUG_OBJECT (demux, "Cues located at offset %" G_GUINT64_FORMAT,
demux->index_offset);
}
}
/* seek */
- demux->offset = seek_pos + demux->ebml_segment_start;
+ demux->offset = seek_pos + demux->common.ebml_segment_start;
/* check ID */
if ((ret = gst_matroska_demux_peek_id_length_pull (demux, &id, &length,
if (id != seek_id) {
GST_WARNING_OBJECT (demux,
"We looked for ID=0x%x but got ID=0x%x (pos=%" G_GUINT64_FORMAT ")",
- seek_id, id, seek_pos + demux->ebml_segment_start);
+ seek_id, id, seek_pos + demux->common.ebml_segment_start);
} else {
/* now parse */
ret = gst_matroska_demux_parse_id (demux, id, length, needed);
case GST_MATROSKA_ID_CLUSTER:
{
- guint64 pos = seek_pos + demux->ebml_segment_start;
+ guint64 pos = seek_pos + demux->common.ebml_segment_start;
GST_LOG_OBJECT (demux, "Cluster position");
if (G_UNLIKELY (!demux->clusters))
}
default:
- ret = gst_matroska_demux_parse_skip (demux, ebml, "SeekHead", id);
+ ret = gst_matroska_read_common_parse_skip (&demux->common,
+ ebml, "SeekHead", id);
break;
}
}
if (G_LIKELY (length != G_MAXUINT64))
read += needed;
- switch (demux->state) {
- case GST_MATROSKA_DEMUX_STATE_START:
+ switch (demux->common.state) {
+ case GST_MATROSKA_READ_STATE_START:
switch (id) {
case GST_EBML_ID_HEADER:
GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
ret = gst_matroska_demux_parse_header (demux, &ebml);
if (ret != GST_FLOW_OK)
goto parse_failed;
- demux->state = GST_MATROSKA_DEMUX_STATE_SEGMENT;
+ demux->common.state = GST_MATROSKA_READ_STATE_SEGMENT;
gst_matroska_demux_check_seekability (demux);
break;
default:
break;
}
break;
- case GST_MATROSKA_DEMUX_STATE_SEGMENT:
+ case GST_MATROSKA_READ_STATE_SEGMENT:
switch (id) {
case GST_MATROSKA_ID_SEGMENT:
/* eat segment prefix */
demux->offset);
/* seeks are from the beginning of the segment,
* after the segment ID/length */
- demux->ebml_segment_start = demux->offset;
- demux->state = GST_MATROSKA_DEMUX_STATE_HEADER;
+ demux->common.ebml_segment_start = demux->offset;
+ demux->common.state = GST_MATROSKA_READ_STATE_HEADER;
break;
default:
GST_WARNING_OBJECT (demux,
break;
}
break;
- case GST_MATROSKA_DEMUX_STATE_SCANNING:
+ case GST_MATROSKA_READ_STATE_SCANNING:
if (id != GST_MATROSKA_ID_CLUSTER &&
id != GST_MATROSKA_ID_CLUSTERTIMECODE)
goto skip;
/* fall-through */
- case GST_MATROSKA_DEMUX_STATE_HEADER:
- case GST_MATROSKA_DEMUX_STATE_DATA:
- case GST_MATROSKA_DEMUX_STATE_SEEK:
+ case GST_MATROSKA_READ_STATE_HEADER:
+ case GST_MATROSKA_READ_STATE_DATA:
+ case GST_MATROSKA_READ_STATE_SEEK:
switch (id) {
case GST_MATROSKA_ID_SEGMENTINFO:
if (!demux->segmentinfo_parsed) {
goto no_tracks;
}
}
- if (G_UNLIKELY (demux->state == GST_MATROSKA_DEMUX_STATE_HEADER)) {
- demux->state = GST_MATROSKA_DEMUX_STATE_DATA;
+ if (G_UNLIKELY (demux->common.state
+ == GST_MATROSKA_READ_STATE_HEADER)) {
+ demux->common.state = GST_MATROSKA_READ_STATE_DATA;
demux->first_cluster_offset = demux->offset;
GST_DEBUG_OBJECT (demux, "signaling no more pads");
gst_element_no_more_pads (GST_ELEMENT (demux));
goto parse_failed;
GST_DEBUG_OBJECT (demux, "ClusterTimeCode: %" G_GUINT64_FORMAT, num);
demux->cluster_time = num;
- if (demux->element_index) {
- if (demux->element_index_writer_id == -1)
- gst_index_get_writer_id (demux->element_index,
- GST_OBJECT (demux), &demux->element_index_writer_id);
+ if (demux->common.element_index) {
+ if (demux->common.element_index_writer_id == -1)
+ gst_index_get_writer_id (demux->common.element_index,
+ GST_OBJECT (demux), &demux->common.element_index_writer_id);
GST_LOG_OBJECT (demux, "adding association %" GST_TIME_FORMAT "-> %"
G_GUINT64_FORMAT " for writer id %d",
GST_TIME_ARGS (demux->cluster_time), demux->cluster_offset,
- demux->element_index_writer_id);
- gst_index_add_association (demux->element_index,
- demux->element_index_writer_id, GST_ASSOCIATION_FLAG_KEY_UNIT,
+ demux->common.element_index_writer_id);
+ gst_index_add_association (demux->common.element_index,
+ demux->common.element_index_writer_id,
+ GST_ASSOCIATION_FLAG_KEY_UNIT,
GST_FORMAT_TIME, demux->cluster_time,
GST_FORMAT_BYTES, demux->cluster_offset, NULL);
}
ret = gst_matroska_demux_parse_contents (demux, &ebml);
break;
case GST_MATROSKA_ID_CUES:
- if (demux->index_parsed) {
+ if (demux->common.index_parsed) {
GST_READ_CHECK (gst_matroska_demux_flush (demux, read));
break;
}
GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
- ret = gst_matroska_demux_parse_index (demux, &ebml);
+ ret = gst_matroska_read_common_parse_index (&demux->common, &ebml);
/* only push based; delayed index building */
if (ret == GST_FLOW_OK
- && demux->state == GST_MATROSKA_DEMUX_STATE_SEEK) {
+ && demux->common.state == GST_MATROSKA_READ_STATE_SEEK) {
GstEvent *event;
GST_OBJECT_LOCK (demux);
goto seek_failed;
/* resume data handling, main thread clear to seek again */
GST_OBJECT_LOCK (demux);
- demux->state = GST_MATROSKA_DEMUX_STATE_DATA;
+ demux->common.state = GST_MATROSKA_READ_STATE_DATA;
GST_OBJECT_UNLOCK (demux);
}
break;
guint needed;
/* If we have to close a segment, send a new segment to do this now */
- if (G_LIKELY (demux->state == GST_MATROSKA_DEMUX_STATE_DATA)) {
+ if (G_LIKELY (demux->common.state == GST_MATROSKA_READ_STATE_DATA)) {
if (G_UNLIKELY (demux->close_segment)) {
gst_matroska_demux_send_event (demux, demux->close_segment);
demux->close_segment = NULL;
goto pause;
/* check if we're at the end of a configured segment */
- if (G_LIKELY (demux->src->len)) {
+ if (G_LIKELY (demux->common.src->len)) {
guint i;
- g_assert (demux->num_streams == demux->src->len);
- for (i = 0; i < demux->src->len; i++) {
- GstMatroskaTrackContext *context = g_ptr_array_index (demux->src, i);
+ g_assert (demux->common.num_streams == demux->common.src->len);
+ for (i = 0; i < demux->common.src->len; i++) {
+ GstMatroskaTrackContext *context = g_ptr_array_index (demux->common.src,
+ i);
GST_DEBUG_OBJECT (context->pad, "pos %" GST_TIME_FORMAT,
GST_TIME_ARGS (context->pos));
if (context->eos == FALSE)
"received format %d newsegment %" GST_SEGMENT_FORMAT, format,
&segment);
- if (demux->state < GST_MATROSKA_DEMUX_STATE_DATA) {
+ if (demux->common.state < GST_MATROSKA_READ_STATE_DATA) {
GST_DEBUG_OBJECT (demux, "still starting");
goto exit;
}
}
case GST_EVENT_EOS:
{
- if (demux->state != GST_MATROSKA_DEMUX_STATE_DATA) {
+ if (demux->common.state != GST_MATROSKA_READ_STATE_DATA) {
gst_event_unref (event);
GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
(NULL), ("got eos and didn't receive a complete header object"));
- } else if (demux->num_streams == 0) {
+ } else if (demux->common.num_streams == 0) {
GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
(NULL), ("got eos but no streams (yet)"));
} else {
GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element);
GST_OBJECT_LOCK (demux);
- if (demux->element_index)
- gst_object_unref (demux->element_index);
- demux->element_index = index ? gst_object_ref (index) : NULL;
+ if (demux->common.element_index)
+ gst_object_unref (demux->common.element_index);
+ demux->common.element_index = index ? gst_object_ref (index) : NULL;
GST_OBJECT_UNLOCK (demux);
- GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT, demux->element_index);
+ GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT,
+ demux->common.element_index);
}
static GstIndex *
GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element);
GST_OBJECT_LOCK (demux);
- if (demux->element_index)
- result = gst_object_ref (demux->element_index);
+ if (demux->common.element_index)
+ result = gst_object_ref (demux->common.element_index);
GST_OBJECT_UNLOCK (demux);
GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
/* GStreamer Matroska muxer/demuxer
* (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * (c) 2011 Debarshi Ray <rishi@gnu.org>
*
* matroska-demux.h: matroska file/stream demuxer definition
*
#include "ebml-read.h"
#include "matroska-ids.h"
+#include "matroska-read-common.h"
G_BEGIN_DECLS
#define GST_IS_MATROSKA_DEMUX_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MATROSKA_DEMUX))
-typedef enum {
- GST_MATROSKA_DEMUX_STATE_START,
- GST_MATROSKA_DEMUX_STATE_SEGMENT,
- GST_MATROSKA_DEMUX_STATE_HEADER,
- GST_MATROSKA_DEMUX_STATE_DATA,
- GST_MATROSKA_DEMUX_STATE_SEEK,
- GST_MATROSKA_DEMUX_STATE_SCANNING
-} GstMatroskaDemuxState;
-
typedef struct _GstMatroskaDemux {
GstElement parent;
/* < private > */
- GstIndex *element_index;
- gint element_index_writer_id;
+ GstMatroskaReadCommon common;
/* pads */
GstPad *sinkpad;
- GPtrArray *src;
GstClock *clock;
- guint num_streams;
guint num_v_streams;
guint num_a_streams;
guint num_t_streams;
/* state */
gboolean streaming;
- GstMatroskaDemuxState 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;
/* cluster positions (optional) */
GArray *clusters;
- /* timescale in the file */
- guint64 time_scale;
-
/* keeping track of playback position */
GstSegment segment;
gboolean segment_running;
* (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
* (c) 2006 Tim-Philipp Müller <tim centricular net>
* (c) 2008 Sebastian Dröge <slomo@circular-chaos.org>
+ * (c) 2011 Debarshi Ray <rishi@gnu.org>
*
* matroska-parse.c: matroska file/stream parser
*
#include <gst/base/gsttypefindhelper.h>
-#ifdef HAVE_ZLIB
-#include <zlib.h>
-#endif
-
-#ifdef HAVE_BZ2
-#include <bzlib.h>
-#endif
-
#include <gst/pbutils/pbutils.h>
-#include "lzo.h"
-
#include "matroska-parse.h"
#include "matroska-ids.h"
{
GstMatroskaParse *parse = GST_MATROSKA_PARSE (object);
- if (parse->src) {
- g_ptr_array_free (parse->src, TRUE);
- parse->src = NULL;
+ if (parse->common.src) {
+ g_ptr_array_free (parse->common.src, TRUE);
+ parse->common.src = NULL;
}
if (parse->global_tags) {
gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad);
/* initial stream no. */
- parse->src = NULL;
+ parse->common.src = NULL;
parse->writing_app = NULL;
parse->muxing_app = NULL;
- parse->index = NULL;
+ parse->common.index = NULL;
parse->global_tags = NULL;
parse->adapter = gst_adapter_new ();
GST_DEBUG_OBJECT (parse, "Resetting state");
/* reset input */
- parse->state = GST_MATROSKA_PARSE_STATE_START;
+ parse->common.state = GST_MATROSKA_READ_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);
+ if (parse->common.src) {
+ g_assert (parse->common.src->len == parse->common.num_streams);
+ for (i = 0; i < parse->common.src->len; i++) {
+ GstMatroskaTrackContext *context = g_ptr_array_index (parse->common.src,
+ i);
gst_caps_replace (&context->caps, NULL);
gst_matroska_track_free (context);
}
- g_ptr_array_free (parse->src, TRUE);
+ g_ptr_array_free (parse->common.src, TRUE);
}
- parse->src = g_ptr_array_new ();
+ parse->common.src = g_ptr_array_new ();
- parse->num_streams = 0;
+ parse->common.num_streams = 0;
parse->num_a_streams = 0;
parse->num_t_streams = 0;
parse->num_v_streams = 0;
parse->muxing_app = NULL;
/* reset indexes */
- if (parse->index) {
- g_array_free (parse->index, TRUE);
- parse->index = NULL;
+ if (parse->common.index) {
+ g_array_free (parse->common.index, TRUE);
+ parse->common.index = NULL;
}
/* reset timers */
parse->clock = NULL;
- parse->time_scale = 1000000;
+ parse->common.time_scale = 1000000;
parse->created = G_MININT64;
- parse->index_parsed = FALSE;
+ parse->common.index_parsed = FALSE;
parse->tracks_parsed = FALSE;
parse->segmentinfo_parsed = FALSE;
parse->attachments_parsed = FALSE;
parse->new_segment = NULL;
}
- if (parse->element_index) {
- gst_object_unref (parse->element_index);
- parse->element_index = NULL;
+ if (parse->common.element_index) {
+ gst_object_unref (parse->common.element_index);
+ parse->common.element_index = NULL;
}
- parse->element_index_writer_id = -1;
+ parse->common.element_index_writer_id = -1;
if (parse->global_tags) {
gst_tag_list_free (parse->global_tags);
}
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)
{
}
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)
{
}
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);
- 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;
-
- 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)
{
{
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);
+ g_assert (parse->common.src->len == parse->common.num_streams);
+ for (i = 0; i < parse->common.src->len; i++) {
+ GstMatroskaTrackContext *context = g_ptr_array_index (parse->common.src,
+ i);
if (context->num == num)
return FALSE;
/* 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;
+ g_ptr_array_add (parse->common.src, context);
+ context->index = parse->common.num_streams;
context->index_writer_id = -1;
context->type = 0; /* no type yet */
context->default_duration = 0;
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);
+ parse->common.num_streams++;
+ g_assert (parse->common.src->len == parse->common.num_streams);
GST_DEBUG_OBJECT (parse, "Stream number %d", context->index);
context->type = 0;
break;
}
- g_ptr_array_index (parse->src, parse->num_streams - 1) = context;
+ g_ptr_array_index (parse->common.src, parse->common.num_streams - 1)
+ = context;
break;
}
break;
}
videocontext = (GstMatroskaTrackVideoContext *) context;
- g_ptr_array_index (parse->src, parse->num_streams - 1) = context;
+ g_ptr_array_index (parse->common.src, parse->common.num_streams - 1)
+ = context;
while (ret == GST_FLOW_OK &&
gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
break;
audiocontext = (GstMatroskaTrackAudioContext *) context;
- g_ptr_array_index (parse->src, parse->num_streams - 1) = context;
+ g_ptr_array_index (parse->common.src, parse->common.num_streams - 1)
+ = context;
while (ret == GST_FLOW_OK &&
gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
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);
+ parse->common.num_streams--;
+ g_ptr_array_remove_index (parse->common.src, parse->common.num_streams);
+ g_assert (parse->common.src->len == parse->common.num_streams);
if (context) {
gst_matroska_track_free (context);
}
GstMatroskaIndex *entry = NULL;
GArray *index;
- if (!parse->index || !parse->index->len)
+ if (!parse->common.index || !parse->common.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;
+ index = parse->common.index;
entry =
gst_util_array_binary_search (index->data, index->len,
if (track && track->type == GST_MATROSKA_TRACK_TYPE_VIDEO)
return track;
- for (i = 0; i < parse->src->len; i++) {
+ for (i = 0; i < parse->common.src->len; i++) {
GstMatroskaTrackContext *stream;
- stream = g_ptr_array_index (parse->src, i);
+ stream = g_ptr_array_index (parse->common.src, i);
if (stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO && stream->index_table)
track = stream;
}
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);
+ g_assert (parse->common.src->len == parse->common.num_streams);
+ for (i = 0; i < parse->common.src->len; i++) {
+ GstMatroskaTrackContext *context = g_ptr_array_index (parse->common.src,
+ i);
context->pos = time;
context->set_discont = TRUE;
context->eos = FALSE;
/* 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);
+ return perform_seek_to_offset (parse, entry->pos
+ + parse->common.ebml_segment_start);
}
/*
}
/* check for having parsed index already */
- if (!parse->index_parsed) {
+ if (!parse->common.index_parsed) {
gboolean building_index;
guint64 offset = 0;
GST_OBJECT_LOCK (parse);
/* handle the seek event in the chain function */
- parse->state = GST_MATROSKA_PARSE_STATE_SEEK;
+ parse->common.state = GST_MATROSKA_READ_STATE_SEEK;
/* no more seek can be issued until state reset to _DATA */
/* copy the event */
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
/* no seeking until we are (safely) ready */
- if (parse->state != GST_MATROSKA_PARSE_STATE_DATA) {
+ if (parse->common.state != GST_MATROSKA_READ_STATE_DATA) {
GST_DEBUG_OBJECT (parse, "not ready for seeking yet");
return FALSE;
}
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)
{
}
default:
- ret = gst_matroska_parse_parse_skip (parse, ebml, "EBML header", id);
+ ret = gst_matroska_read_common_parse_skip (&parse->common, ebml,
+ "EBML header", id);
if (ret != GST_FLOW_OK)
return ret;
break;
break;
default:
- ret = gst_matroska_parse_parse_skip (parse, ebml, "Track", id);
+ ret = gst_matroska_read_common_parse_skip (&parse->common, ebml,
+ "Track", id);
break;
}
}
}
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;
GST_DEBUG_OBJECT (parse, "TimeCodeScale: %" G_GUINT64_FORMAT, num);
- parse->time_scale = num;
+ parse->common.time_scale = num;
break;
}
}
default:
- ret = gst_matroska_parse_parse_skip (parse, ebml, "SegmentInfo", id);
+ ret = gst_matroska_read_common_parse_skip (&parse->common, ebml,
+ "SegmentInfo", id);
break;
/* fall through */
GstClockTime dur_u;
dur_u = gst_gdouble_to_guint64 (dur_f *
- gst_guint64_to_gdouble (parse->time_scale));
+ gst_guint64_to_gdouble (parse->common.time_scale));
if (GST_CLOCK_TIME_IS_VALID (dur_u) && dur_u <= G_MAXINT64)
gst_segment_set_duration (&parse->segment, GST_FORMAT_TIME, dur_u);
}
break;
default:
- ret = gst_matroska_parse_parse_skip (parse, ebml, "SimpleTag", id);
+ ret = gst_matroska_read_common_parse_skip (&parse->common, ebml,
+ "SimpleTag", id);
break;
/* fall-through */
break;
default:
- ret = gst_matroska_parse_parse_skip (parse, ebml, "Tag", id);
+ ret = gst_matroska_read_common_parse_skip (&parse->common, ebml,
+ "Tag", id);
break;
}
}
break;
default:
- ret = gst_matroska_parse_parse_skip (parse, ebml, "Tags", id);
+ ret = gst_matroska_read_common_parse_skip (&parse->common, ebml,
+ "Tags", id);
break;
/* FIXME: Use to limit the tags to specific pads */
case GST_MATROSKA_ID_TARGETS:
break;
default:
- ret = gst_matroska_parse_parse_skip (parse, ebml, "AttachedFile", id);
+ ret = gst_matroska_read_common_parse_skip (&parse->common, ebml,
+ "AttachedFile", id);
break;
case GST_MATROSKA_ID_FILEUID:
ret = gst_ebml_read_skip (ebml);
break;
default:
- ret = gst_matroska_parse_parse_skip (parse, ebml, "Attachments", id);
+ ret = gst_matroska_read_common_parse_skip (&parse->common, ebml,
+ "Attachments", id);
break;
}
}
size -= n;
/* fetch stream from num */
- stream_num = gst_matroska_parse_stream_from_num (parse, num);
+ stream_num = gst_matroska_read_common_stream_from_num (&parse->common,
+ 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)) {
+ stream_num >= parse->common.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
goto done;
}
- stream = g_ptr_array_index (parse->src, stream_num);
+ stream = g_ptr_array_index (parse->common.src, stream_num);
/* time (relative to cluster time) */
time = ((gint16) GST_READ_UINT16_BE (data));
}
default:
- ret = gst_matroska_parse_parse_skip (parse, ebml, "BlockGroup", id);
+ ret = gst_matroska_read_common_parse_skip (&parse->common, ebml,
+ "BlockGroup", id);
break;
case GST_MATROSKA_ID_BLOCKVIRTUAL:
gint64 lace_time = 0;
gboolean delta_unit;
- stream = g_ptr_array_index (parse->src, stream_num);
+ stream = g_ptr_array_index (parse->common.src, stream_num);
if (cluster_time != GST_CLOCK_TIME_NONE) {
/* FIXME: What to do with negative timestamps? Give timestamp 0 or -1?
lace_time = 0;
} else {
if (stream->timecodescale == 1.0)
- lace_time = (cluster_time + time) * parse->time_scale;
+ lace_time = (cluster_time + time) * parse->common.time_scale;
else
lace_time =
gst_util_guint64_to_gdouble ((cluster_time + time) *
- parse->time_scale) * stream->timecodescale;
+ parse->common.time_scale) * stream->timecodescale;
}
} else {
lace_time = GST_CLOCK_TIME_NONE;
if (block_duration) {
if (stream->timecodescale == 1.0)
- duration = gst_util_uint64_scale (block_duration, parse->time_scale, 1);
+ duration = gst_util_uint64_scale (block_duration,
+ parse->common.time_scale, 1);
else
duration =
gst_util_gdouble_to_guint64 (gst_util_guint64_to_gdouble
- (gst_util_uint64_scale (block_duration, parse->time_scale,
+ (gst_util_uint64_scale (block_duration, parse->common.time_scale,
1)) * stream->timecodescale);
} else if (stream->default_duration) {
duration = stream->default_duration * laces;
}
default:
- ret = gst_matroska_parse_parse_skip (parse, ebml, "SeekHead", id);
+ ret = gst_matroska_read_common_parse_skip (&parse->common, ebml,
+ "SeekHead", id);
break;
}
}
}
/* check for validity */
- if (seek_pos + parse->ebml_segment_start + 12 >= length) {
+ if (seek_pos + parse->common.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);
+ G_GUINT64_FORMAT ")", seek_pos, parse->common.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;
+ parse->index_offset = seek_pos + parse->common.ebml_segment_start;
GST_DEBUG_OBJECT (parse, "Cues located at offset %" G_GUINT64_FORMAT,
parse->index_offset);
}
}
default:
- ret = gst_matroska_parse_parse_skip (parse, ebml, "SeekHead", id);
+ ret = gst_matroska_read_common_parse_skip (&parse->common, ebml,
+ "SeekHead", id);
break;
}
}
if (G_LIKELY (length != G_MAXUINT64))
read += needed;
- switch (parse->state) {
- case GST_MATROSKA_PARSE_STATE_START:
+ switch (parse->common.state) {
+ case GST_MATROSKA_READ_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;
+ parse->common.state = GST_MATROSKA_READ_STATE_SEGMENT;
gst_matroska_parse_check_seekability (parse);
gst_matroska_parse_accumulate_streamheader (parse, ebml.buf);
break;
break;
}
break;
- case GST_MATROSKA_PARSE_STATE_SEGMENT:
+ case GST_MATROSKA_READ_STATE_SEGMENT:
switch (id) {
case GST_MATROSKA_ID_SEGMENT:
/* eat segment prefix */
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;
+ parse->common.ebml_segment_start = parse->offset;
+ parse->common.state = GST_MATROSKA_READ_STATE_HEADER;
gst_matroska_parse_accumulate_streamheader (parse, ebml.buf);
break;
default:
break;
}
break;
- case GST_MATROSKA_PARSE_STATE_SCANNING:
+ case GST_MATROSKA_READ_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:
+ case GST_MATROSKA_READ_STATE_HEADER:
+ case GST_MATROSKA_READ_STATE_DATA:
+ case GST_MATROSKA_READ_STATE_SEEK:
switch (id) {
case GST_MATROSKA_ID_SEGMENTINFO:
GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml));
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;
+ if (G_UNLIKELY (parse->common.state
+ == GST_MATROSKA_READ_STATE_HEADER)) {
+ parse->common.state = GST_MATROSKA_READ_STATE_DATA;
parse->first_cluster_offset = parse->offset;
GST_DEBUG_OBJECT (parse, "signaling no more pads");
}
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);
+ if (parse->common.element_index) {
+ if (parse->common.element_index_writer_id == -1)
+ gst_index_get_writer_id (parse->common.element_index,
+ GST_OBJECT (parse), &parse->common.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,
+ parse->common.element_index_writer_id);
+ gst_index_add_association (parse->common.element_index,
+ parse->common.element_index_writer_id,
+ GST_ASSOCIATION_FLAG_KEY_UNIT,
GST_FORMAT_TIME, parse->cluster_time,
GST_FORMAT_BYTES, parse->cluster_offset, NULL);
}
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);
+ if (!parse->common.index_parsed) {
+ ret = gst_matroska_read_common_parse_index (&parse->common, &ebml);
/* only push based; delayed index building */
if (ret == GST_FLOW_OK
- && parse->state == GST_MATROSKA_PARSE_STATE_SEEK) {
+ && parse->common.state == GST_MATROSKA_READ_STATE_SEEK) {
GstEvent *event;
GST_OBJECT_LOCK (parse);
goto seek_failed;
/* resume data handling, main thread clear to seek again */
GST_OBJECT_LOCK (parse);
- parse->state = GST_MATROSKA_PARSE_STATE_DATA;
+ parse->common.state = GST_MATROSKA_READ_STATE_DATA;
GST_OBJECT_UNLOCK (parse);
}
}
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_LIKELY (parse->common.state == GST_MATROSKA_READ_STATE_DATA)) {
if (G_UNLIKELY (parse->close_segment)) {
gst_matroska_parse_send_event (parse, parse->close_segment);
parse->close_segment = NULL;
"received format %d newsegment %" GST_SEGMENT_FORMAT, format,
&segment);
- if (parse->state < GST_MATROSKA_PARSE_STATE_DATA) {
+ if (parse->common.state < GST_MATROSKA_READ_STATE_DATA) {
GST_DEBUG_OBJECT (parse, "still starting");
goto exit;
}
}
case GST_EVENT_EOS:
{
- if (parse->state != GST_MATROSKA_PARSE_STATE_DATA) {
+ if (parse->common.state != GST_MATROSKA_READ_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) {
+ } else if (parse->common.num_streams == 0) {
GST_ELEMENT_ERROR (parse, STREAM, DEMUX,
(NULL), ("got eos but no streams (yet)"));
} else {
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;
+ if (parse->common.element_index)
+ gst_object_unref (parse->common.element_index);
+ parse->common.element_index = index ? gst_object_ref (index) : NULL;
GST_OBJECT_UNLOCK (parse);
- GST_DEBUG_OBJECT (parse, "Set index %" GST_PTR_FORMAT, parse->element_index);
+ GST_DEBUG_OBJECT (parse, "Set index %" GST_PTR_FORMAT,
+ parse->common.element_index);
}
static GstIndex *
GstMatroskaParse *parse = GST_MATROSKA_PARSE (element);
GST_OBJECT_LOCK (parse);
- if (parse->element_index)
- result = gst_object_ref (parse->element_index);
+ if (parse->common.element_index)
+ result = gst_object_ref (parse->common.element_index);
GST_OBJECT_UNLOCK (parse);
GST_DEBUG_OBJECT (parse, "Returning index %" GST_PTR_FORMAT, result);
/* GStreamer Matroska muxer/demuxer
* (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * (c) 2011 Debarshi Ray <rishi@gnu.org>
*
* matroska-parse.h: matroska file/stream parseer definition
*
#include "ebml-read.h"
#include "matroska-ids.h"
+#include "matroska-read-common.h"
G_BEGIN_DECLS
#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;
+ GstMatroskaReadCommon common;
/* pads */
GstPad *sinkpad;
GstPad *srcpad;
- GPtrArray *src;
GstClock *clock;
- guint num_streams;
guint num_v_streams;
guint num_a_streams;
guint num_t_streams;
/* 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;
--- /dev/null
+/* GStreamer Matroska muxer/demuxer
+ * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * (c) 2006 Tim-Philipp Müller <tim centricular net>
+ * (c) 2008 Sebastian Dröge <slomo@circular-chaos.org>
+ * (c) 2011 Debarshi Ray <rishi@gnu.org>
+ *
+ * matroska-read-common.c: shared by matroska file/stream demuxer and 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#ifdef HAVE_ZLIB
+#include <zlib.h>
+#endif
+
+#ifdef HAVE_BZ2
+#include <bzlib.h>
+#endif
+
+#include "lzo.h"
+
+#include "ebml-read.h"
+#include "matroska-read-common.h"
+
+GST_DEBUG_CATEGORY_STATIC (matroskareadcommon_debug);
+#define GST_CAT_DEFAULT matroskareadcommon_debug
+
+#define DEBUG_ELEMENT_START(common, ebml, element) \
+ GST_DEBUG_OBJECT (common, "Parsing " element " element at offset %" \
+ G_GUINT64_FORMAT, gst_ebml_read_get_pos (ebml))
+
+#define DEBUG_ELEMENT_STOP(common, ebml, element, ret) \
+ GST_DEBUG_OBJECT (common, "Parsing " element " element " \
+ " finished with '%s'", gst_flow_get_name (ret))
+
+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);
+ 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;
+
+ 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;
+}
+
+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 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;
+}
+
+/* skip unknown or alike element */
+GstFlowReturn
+gst_matroska_read_common_parse_skip (GstMatroskaReadCommon * common,
+ GstEbmlRead * ebml, const gchar * parent_name, guint id)
+{
+ if (id == GST_EBML_ID_VOID) {
+ GST_DEBUG_OBJECT (common, "Skipping EBML Void element");
+ } else if (id == GST_EBML_ID_CRC32) {
+ GST_DEBUG_OBJECT (common, "Skipping EBML CRC32 element");
+ } else {
+ GST_WARNING_OBJECT (common,
+ "Unknown %s subelement 0x%x - ignoring", parent_name, id);
+ }
+
+ return gst_ebml_read_skip (ebml);
+}
+
+static GstFlowReturn
+gst_matroska_read_common_parse_index_cuetrack (GstMatroskaReadCommon * common,
+ 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 (common, ebml, "CueTrackPositions");
+
+ if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
+ DEBUG_ELEMENT_STOP (common, 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 (common, "Invalid CueTrack 0");
+ break;
+ }
+
+ GST_DEBUG_OBJECT (common, "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 (common, "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 (common, "Invalid CueBlockNumber 0");
+ break;
+ }
+
+ GST_DEBUG_OBJECT (common, "CueBlockNumber: %" G_GUINT64_FORMAT, num);
+ idx.block = num;
+
+ /* mild sanity check, disregard strange cases ... */
+ if (idx.block > G_MAXUINT16) {
+ GST_DEBUG_OBJECT (common, "... looks suspicious, ignoring");
+ idx.block = 1;
+ }
+ break;
+ }
+
+ default:
+ ret = gst_matroska_read_common_parse_skip (common, ebml,
+ "CueTrackPositions", id);
+ break;
+
+ case GST_MATROSKA_ID_CUECODECSTATE:
+ case GST_MATROSKA_ID_CUEREFERENCE:
+ ret = gst_ebml_read_skip (ebml);
+ break;
+ }
+ }
+
+ DEBUG_ELEMENT_STOP (common, ebml, "CueTrackPositions", ret);
+
+ if ((ret == GST_FLOW_OK || ret == GST_FLOW_UNEXPECTED)
+ && idx.pos != (guint64) - 1 && idx.track > 0) {
+ g_array_append_val (common->index, idx);
+ (*nentries)++;
+ } else if (ret == GST_FLOW_OK || ret == GST_FLOW_UNEXPECTED) {
+ GST_DEBUG_OBJECT (common, "CueTrackPositions without valid content");
+ }
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_matroska_read_common_parse_index_pointentry (GstMatroskaReadCommon *
+ common, GstEbmlRead * ebml)
+{
+ guint32 id;
+ GstFlowReturn ret;
+ GstClockTime time = GST_CLOCK_TIME_NONE;
+ guint nentries = 0;
+
+ DEBUG_ELEMENT_START (common, ebml, "CuePoint");
+
+ if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
+ DEBUG_ELEMENT_STOP (common, 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 (common, "CueTime: %" G_GUINT64_FORMAT, time);
+ time = time * common->time_scale;
+ break;
+ }
+
+ /* position in the file + track to which it belongs */
+ case GST_MATROSKA_ID_CUETRACKPOSITIONS:
+ {
+ if ((ret =
+ gst_matroska_read_common_parse_index_cuetrack (common, ebml,
+ &nentries)) != GST_FLOW_OK)
+ break;
+ break;
+ }
+
+ default:
+ ret = gst_matroska_read_common_parse_skip (common, ebml, "CuePoint",
+ id);
+ break;
+ }
+ }
+
+ DEBUG_ELEMENT_STOP (common, ebml, "CuePoint", ret);
+
+ if (nentries > 0) {
+ if (time == GST_CLOCK_TIME_NONE) {
+ GST_WARNING_OBJECT (common, "CuePoint without valid time");
+ g_array_remove_range (common->index, common->index->len - nentries,
+ nentries);
+ } else {
+ gint i;
+
+ for (i = common->index->len - nentries; i < common->index->len; i++) {
+ GstMatroskaIndex *idx =
+ &g_array_index (common->index, GstMatroskaIndex, i);
+
+ idx->time = time;
+ GST_DEBUG_OBJECT (common, "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 (common, "Empty CuePoint");
+ }
+
+ return ret;
+}
+
+gint
+gst_matroska_read_common_stream_from_num (GstMatroskaReadCommon * common,
+ guint track_num)
+{
+ guint n;
+
+ g_assert (common->src->len == common->num_streams);
+ for (n = 0; n < common->src->len; n++) {
+ GstMatroskaTrackContext *context = g_ptr_array_index (common->src, n);
+
+ if (context->num == track_num) {
+ return n;
+ }
+ }
+
+ if (n == common->num_streams)
+ GST_WARNING_OBJECT (common,
+ "Failed to find corresponding pad for tracknum %d", track_num);
+
+ return -1;
+}
+
+GstFlowReturn
+gst_matroska_read_common_parse_index (GstMatroskaReadCommon * common,
+ GstEbmlRead * ebml)
+{
+ guint32 id;
+ GstFlowReturn ret = GST_FLOW_OK;
+ guint i;
+
+ if (common->index)
+ g_array_free (common->index, TRUE);
+ common->index =
+ g_array_sized_new (FALSE, FALSE, sizeof (GstMatroskaIndex), 128);
+
+ DEBUG_ELEMENT_START (common, ebml, "Cues");
+
+ if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
+ DEBUG_ELEMENT_STOP (common, 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_read_common_parse_index_pointentry (common, ebml);
+ break;
+
+ default:
+ ret = gst_matroska_read_common_parse_skip (common, ebml, "Cues", id);
+ break;
+ }
+ }
+ DEBUG_ELEMENT_STOP (common, ebml, "Cues", ret);
+
+ /* Sort index by time, smallest time first, for easier searching */
+ g_array_sort (common->index, (GCompareFunc) gst_matroska_index_compare);
+
+ /* Now sort the track specific index entries into their own arrays */
+ for (i = 0; i < common->index->len; i++) {
+ GstMatroskaIndex *idx = &g_array_index (common->index, GstMatroskaIndex,
+ i);
+ gint track_num;
+ GstMatroskaTrackContext *ctx;
+
+ if (common->element_index) {
+ gint writer_id;
+
+ if (idx->track != 0 &&
+ (track_num =
+ gst_matroska_read_common_stream_from_num (common,
+ idx->track)) != -1) {
+ ctx = g_ptr_array_index (common->src, track_num);
+
+ if (ctx->index_writer_id == -1)
+ gst_index_get_writer_id (common->element_index,
+ GST_OBJECT (ctx->pad), &ctx->index_writer_id);
+ writer_id = ctx->index_writer_id;
+ } else {
+ if (common->element_index_writer_id == -1)
+ gst_index_get_writer_id (common->element_index, GST_OBJECT (common),
+ &common->element_index_writer_id);
+ writer_id = common->element_index_writer_id;
+ }
+
+ GST_LOG_OBJECT (common, "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 (common->element_index, writer_id,
+ GST_ASSOCIATION_FLAG_KEY_UNIT, GST_FORMAT_TIME, idx->time,
+ GST_FORMAT_BYTES, idx->pos + common->ebml_segment_start, NULL);
+ }
+
+ if (idx->track == 0)
+ continue;
+
+ track_num = gst_matroska_read_common_stream_from_num (common, idx->track);
+ if (track_num == -1)
+ continue;
+
+ ctx = g_ptr_array_index (common->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);
+ }
+
+ common->index_parsed = TRUE;
+
+ /* sanity check; empty index normalizes to no index */
+ if (common->index->len == 0) {
+ g_array_free (common->index, TRUE);
+ common->index = NULL;
+ }
+
+ return ret;
+}
--- /dev/null
+/* GStreamer Matroska muxer/demuxer
+ * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * (c) 2011 Debarshi Ray <rishi@gnu.org>
+ *
+ * matroska-read-common.h: shared by matroska file/stream demuxer and 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.
+ */
+
+#ifndef __GST_MATROSKA_READ_COMMON_H__
+#define __GST_MATROSKA_READ_COMMON_H__
+
+#include <glib.h>
+#include <gst/gst.h>
+
+#include "matroska-ids.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GST_MATROSKA_READ_STATE_START,
+ GST_MATROSKA_READ_STATE_SEGMENT,
+ GST_MATROSKA_READ_STATE_HEADER,
+ GST_MATROSKA_READ_STATE_DATA,
+ GST_MATROSKA_READ_STATE_SEEK,
+ GST_MATROSKA_READ_STATE_SCANNING
+} GstMatroskaReadState;
+
+typedef struct _GstMatroskaReadCommon {
+ GstIndex *element_index;
+ gint element_index_writer_id;
+
+ /* pads */
+ GPtrArray *src;
+ guint num_streams;
+
+ /* state */
+ GstMatroskaReadState state;
+
+ /* did we parse cues/tracks/segmentinfo already? */
+ gboolean index_parsed;
+
+ /* start-of-segment */
+ guint64 ebml_segment_start;
+
+ /* a cue (index) table */
+ GArray *index;
+
+ /* timescale in the file */
+ guint64 time_scale;
+} GstMatroskaReadCommon;
+
+GstFlowReturn gst_matroska_decode_content_encodings (GArray * encodings);
+gboolean gst_matroska_decompress_data (GstMatroskaTrackEncoding * enc,
+ guint8 ** data_out, guint * size_out,
+ GstMatroskaTrackCompressionAlgorithm algo);
+GstFlowReturn gst_matroska_read_common_parse_index (GstMatroskaReadCommon *
+ common, GstEbmlRead * ebml);
+GstFlowReturn gst_matroska_read_common_parse_skip (GstMatroskaReadCommon *
+ common, GstEbmlRead * ebml, const gchar * parent_name, guint id);
+gint gst_matroska_read_common_stream_from_num (GstMatroskaReadCommon * common,
+ guint track_num);
+
+G_END_DECLS
+
+#endif /* __GST_MATROSKA_READ_COMMON_H__ */
gst_rtp_buffer_unmap (&rtp);
- GST_DEBUG_OBJECT (rtpac3depay, "pushing buffer of size %d",
- gst_buffer_get_size (outbuf));
+ if (outbuf)
+ GST_DEBUG_OBJECT (rtpac3depay, "pushing buffer of size %d",
+ gst_buffer_get_size (outbuf));
return outbuf;
outbuf = gst_rtp_buffer_get_payload_buffer (&rtp);
gst_rtp_buffer_unmap (&rtp);
- if (marker) {
+ if (marker && outbuf) {
/* mark start of talkspurt with DISCONT */
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
}
marker = gst_rtp_buffer_get_marker (&rtp);
gst_rtp_buffer_unmap (&rtp);
- if (marker) {
+ if (marker && outbuf) {
/* mark talk spurt with DISCONT */
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
}
if (depay->aal2 || depay->force_aal2) {
/* AAL2, we can just copy the bytes */
outbuf = gst_rtp_buffer_get_payload_buffer (&rtp);
+ if (!outbuf)
+ goto bad_len;
} else {
guint8 *in, *out, tmp, *odata;
guint len;
len = gst_rtp_buffer_get_payload_len (&rtp);
outbuf = gst_rtp_buffer_get_payload_buffer (&rtp);
+ if (!outbuf)
+ goto bad_len;
outbuf = gst_buffer_make_writable (outbuf);
odata = gst_buffer_map (outbuf, &osize, NULL, GST_MAP_WRITE);
}
return outbuf;
+
+bad_len:
+ return NULL;
}
static void
gst_rtp_buffer_unmap (&rtp);
- if (marker) {
+ if (marker && outbuf) {
/* mark start of talkspurt with DISCONT */
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
}
gst_rtp_buffer_unmap (&rtp);
- if (marker) {
+ if (marker && outbuf) {
/* mark start of talkspurt with DISCONT */
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
}
guint qt;
qt = info[i].qt;
- if (qt > 15)
+ if (qt >= G_N_ELEMENTS (tables))
goto invalid_quant;
qsize = tables[qt].size;
outbuf = gst_rtp_buffer_get_payload_buffer (buf);
- GST_DEBUG ("gst_rtp_mp1s_depay_chain: pushing buffer of size %d",
- GST_BUFFER_SIZE (outbuf));
+ if (outbuf)
+ GST_DEBUG ("gst_rtp_mp1s_depay_chain: pushing buffer of size %d",
+ GST_BUFFER_SIZE (outbuf));
return outbuf;
}
gst_rtp_buffer_get_payload_subbuffer (buf, rtpmp2tdepay->skip_first_bytes,
-1);
- GST_DEBUG ("gst_rtp_mp2t_depay_chain: pushing buffer of size %d",
- GST_BUFFER_SIZE (outbuf));
+ if (outbuf)
+ GST_DEBUG ("gst_rtp_mp2t_depay_chain: pushing buffer of size %d",
+ GST_BUFFER_SIZE (outbuf));
return outbuf;
/* All optional parameters
*
* "profile-level-id=[1,MAX]"
- * "config="
+ * "config="
*/
)
);
if (!gst_bit_reader_get_bits_uint8 (&br, &sr_idx, 4))
goto bad_config;
- if (sr_idx > 12 && sr_idx != 15) {
+ if (sr_idx >= G_N_ELEMENTS (aac_sample_rates) && sr_idx != 15) {
GST_WARNING_OBJECT (depayload, "invalid sample rate index %d", sr_idx);
goto bad_config;
}
outbuf = gst_rtp_buffer_get_payload_subbuffer (&rtp, payload_header, -1);
- GST_DEBUG_OBJECT (rtpmpvdepay,
- "gst_rtp_mpv_depay_chain: pushing buffer of size %d",
- gst_buffer_get_size (outbuf));
-
+ if (outbuf) {
+ GST_DEBUG_OBJECT (rtpmpvdepay,
+ "gst_rtp_mpv_depay_chain: pushing buffer of size %d",
+ gst_buffer_get_size (outbuf));
+ }
return outbuf;
}
GST_STATIC_CAPS ("application/x-rtp, "
"media = (string) \"audio\", "
"payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
- "clock-rate = (int) 8000, " "encoding-name = (string) \"PCMA\";"
+ "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"PCMA\";"
"application/x-rtp, "
"media = (string) \"audio\", "
"payload = (int) " GST_RTP_PAYLOAD_PCMA_STRING ", "
- "clock-rate = (int) 8000")
+ "clock-rate = (int) [1, MAX ]")
);
static GstStaticPadTemplate gst_rtp_pcma_depay_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-alaw, channels = (int) 1, rate = (int) 8000")
+ GST_STATIC_CAPS ("audio/x-alaw, channels = (int) 1, rate = (int) [1, MAX ]")
);
static GstBuffer *gst_rtp_pcma_depay_process (GstBaseRTPDepayload * depayload,
outbuf = gst_rtp_buffer_get_payload_buffer (&rtp);
gst_rtp_buffer_unmap (&rtp);
- GST_BUFFER_DURATION (outbuf) =
- gst_util_uint64_scale_int (len, GST_SECOND, depayload->clock_rate);
+ if (outbuf) {
+ GST_BUFFER_DURATION (outbuf) =
+ gst_util_uint64_scale_int (len, GST_SECOND, depayload->clock_rate);
- if (marker) {
- /* mark start of talkspurt with DISCONT */
- GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+ if (marker) {
+ /* mark start of talkspurt with DISCONT */
+ GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+ }
}
GST_STATIC_CAPS ("application/x-rtp, "
"media = (string) \"audio\", "
"payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
- "clock-rate = (int) 8000, " "encoding-name = (string) \"PCMU\";"
+ "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"PCMU\";"
"application/x-rtp, "
"media = (string) \"audio\", "
"payload = (int) " GST_RTP_PAYLOAD_PCMU_STRING ", "
- "clock-rate = (int) 8000")
+ "clock-rate = (int) [1, MAX ]")
);
static GstStaticPadTemplate gst_rtp_pcmu_depay_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-mulaw, channels = (int) 1, rate = (int) 8000")
+ GST_STATIC_CAPS ("audio/x-mulaw, "
+ "channels = (int) 1, rate = (int) [1, MAX ]")
);
static GstBuffer *gst_rtp_pcmu_depay_process (GstBaseRTPDepayload * depayload,
outbuf = gst_rtp_buffer_get_payload_buffer (&rtp);
gst_rtp_buffer_unmap (&rtp);
- GST_BUFFER_DURATION (outbuf) =
- gst_util_uint64_scale_int (len, GST_SECOND, depayload->clock_rate);
+ if (outbuf) {
+ GST_BUFFER_DURATION (outbuf) =
+ gst_util_uint64_scale_int (len, GST_SECOND, depayload->clock_rate);
- if (marker) {
- /* mark start of talkspurt with DISCONT */
- GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+ if (marker) {
+ /* mark start of talkspurt with DISCONT */
+ GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+ }
}
return outbuf;
static gint
get_frame_len (GstRtpQCELPDepay * depay, guint8 frame_type)
{
- if (frame_type > 16)
+ if (frame_type >= G_N_ELEMENTS (frame_size))
return 0;
return frame_size[frame_type];
/* nothing special to be done */
outbuf = gst_rtp_buffer_get_payload_buffer (buf);
- GST_BUFFER_DURATION (outbuf) = 20 * GST_MSECOND;
+ if (outbuf)
+ GST_BUFFER_DURATION (outbuf) = 20 * GST_MSECOND;
return outbuf;
}
src->need_redirect = FALSE;
/* can't continue without a valid url */
- if (G_UNLIKELY (src->conninfo.url == NULL))
+ if (G_UNLIKELY (src->conninfo.url == NULL)) {
+ res = GST_RTSP_EINVAL;
goto no_url;
+ }
src->tried_url_auth = FALSE;
if ((res = gst_rtsp_conninfo_connect (src, &src->conninfo, async)) < 0)
#define DEFAULT_PROP_DEVICE_NAME NULL
#define DEFAULT_PROP_DEVICE_FD -1
#define DEFAULT_PROP_FLAGS 0
-#define DEFAULT_PROP_NORM NULL
+#define DEFAULT_PROP_TV_NORM 0
#define DEFAULT_PROP_CHANNEL NULL
#define DEFAULT_PROP_FREQUENCY 0
return v4l2_device_type;
}
+#define GST_TYPE_V4L2_TV_NORM (gst_v4l2_tv_norm_get_type ())
+static GType
+gst_v4l2_tv_norm_get_type (void)
+{
+ static GType v4l2_tv_norm = 0;
+
+ if (!v4l2_tv_norm) {
+ static const GEnumValue tv_norms[] = {
+ {0, "none", "none"},
+
+ {V4L2_STD_NTSC, "NTSC", "NTSC"},
+ {V4L2_STD_NTSC_M, "NTSC-M", "NTSC-M"},
+ {V4L2_STD_NTSC_M_JP, "NTSC-M-JP", "NTSC-M-JP"},
+ {V4L2_STD_NTSC_M_KR, "NTSC-M-KR", "NTSC-M-KR"},
+ {V4L2_STD_NTSC_443, "NTSC-443", "NTSC-443"},
+
+ {V4L2_STD_PAL, "PAL", "PAL"},
+ {V4L2_STD_PAL_BG, "PAL-BG", "PAL-BG"},
+ {V4L2_STD_PAL_B, "PAL-B", "PAL-B"},
+ {V4L2_STD_PAL_B1, "PAL-B1", "PAL-B1"},
+ {V4L2_STD_PAL_G, "PAL-G", "PAL-G"},
+ {V4L2_STD_PAL_H, "PAL-H", "PAL-H"},
+ {V4L2_STD_PAL_I, "PAL-I", "PAL-I"},
+ {V4L2_STD_PAL_DK, "PAL-DK", "PAL-DK"},
+ {V4L2_STD_PAL_D, "PAL-D", "PAL-D"},
+ {V4L2_STD_PAL_D1, "PAL-D1", "PAL-D1"},
+ {V4L2_STD_PAL_K, "PAL-K", "PAL-K"},
+ {V4L2_STD_PAL_M, "PAL-M", "PAL-M"},
+ {V4L2_STD_PAL_N, "PAL-N", "PAL-N"},
+ {V4L2_STD_PAL_Nc, "PAL-Nc", "PAL-Nc"},
+ {V4L2_STD_PAL_60, "PAL-60", "PAL-60"},
+
+ {V4L2_STD_SECAM, "SECAM", "SECAM"},
+ {V4L2_STD_SECAM_B, "SECAM-B", "SECAM-B"},
+ {V4L2_STD_SECAM_G, "SECAM-G", "SECAM-G"},
+ {V4L2_STD_SECAM_H, "SECAM-H", "SECAM-H"},
+ {V4L2_STD_SECAM_DK, "SECAM-DK", "SECAM-DK"},
+ {V4L2_STD_SECAM_D, "SECAM-D", "SECAM-D"},
+ {V4L2_STD_SECAM_K, "SECAM-K", "SECAM-K"},
+ {V4L2_STD_SECAM_K1, "SECAM-K1", "SECAM-K1"},
+ {V4L2_STD_SECAM_L, "SECAM-L", "SECAM-L"},
+ {V4L2_STD_SECAM_LC, "SECAM-Lc", "SECAM-Lc"},
+
+ {0, NULL, NULL}
+ };
+
+ v4l2_tv_norm = g_enum_register_static ("V4L2_TV_norms", tv_norms);
+ }
+
+ return v4l2_tv_norm;
+}
+
void
gst_v4l2_object_install_properties_helper (GObjectClass * gobject_class,
const char *default_device)
"Hue or color balance", G_MININT,
G_MAXINT, 0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
+
+ /**
+ * GstV4l2Src:norm
+ *
+ * TV norm
+ *
+ * Since: 0.10.30
+ */
+ g_object_class_install_property (gobject_class, PROP_TV_NORM,
+ g_param_spec_enum ("norm", "TV norm",
+ "video standard",
+ GST_TYPE_V4L2_TV_NORM, DEFAULT_PROP_TV_NORM,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
GstV4l2Object *
if (v4l2object->channel)
g_free (v4l2object->channel);
- if (v4l2object->norm)
- g_free (v4l2object->norm);
-
if (v4l2object->formats) {
gst_v4l2_object_clear_format_list (v4l2object);
}
return TRUE;
}
break;
-#if 0
- case PROP_NORM:
- if (GST_V4L2_IS_OPEN (v4l2object)) {
- GstTuner *tuner = GST_TUNER (v4l2object->element);
- GstTunerNorm *norm = gst_tuner_find_norm_by_name (tuner,
- (gchar *) g_value_get_string (value));
-
- if (norm) {
- /* like gst_tuner_set_norm (tuner, norm)
- without g_object_notify */
- gst_v4l2_tuner_set_norm (v4l2object, norm);
- }
- } else {
- g_free (v4l2object->norm);
- v4l2object->norm = g_value_dup_string (value);
- }
+ case PROP_TV_NORM:
+ v4l2object->tv_norm = g_value_get_enum (value);
break;
+#if 0
case PROP_CHANNEL:
if (GST_V4L2_IS_OPEN (v4l2object)) {
GstTuner *tuner = GST_TUNER (v4l2object->element);
return TRUE;
}
break;
+ case PROP_TV_NORM:
+ g_value_set_enum (value, v4l2object->tv_norm);
+ break;
default:
return FALSE;
break;
tuner = GST_TUNER (v4l2object->element);
- if (v4l2object->norm)
- norm = gst_tuner_find_norm_by_name (tuner, v4l2object->norm);
+ if (v4l2object->tv_norm)
+ norm = gst_v4l2_tuner_get_norm_by_std_id (v4l2object, v4l2object->tv_norm);
+ GST_DEBUG_OBJECT (v4l2object->element, "tv_norm=%d, norm=%p",
+ v4l2object->tv_norm, norm);
if (norm) {
gst_tuner_set_norm (tuner, norm);
} else {
norm =
GST_TUNER_NORM (gst_tuner_get_norm (GST_TUNER (v4l2object->element)));
if (norm) {
- g_free (v4l2object->norm);
- v4l2object->norm = g_strdup (norm->label);
+ v4l2object->tv_norm =
+ gst_v4l2_tuner_get_std_id_by_norm (v4l2object, norm);
gst_tuner_norm_changed (tuner, norm);
}
}
}
/* Since we can't get framerate directly, try to use the current norm */
- if (v4l2object->norm && v4l2object->norms) {
+ if (v4l2object->tv_norm && v4l2object->norms) {
GList *norms;
GstTunerNorm *norm = NULL;
+ GstTunerNorm *current =
+ gst_v4l2_tuner_get_norm_by_std_id (v4l2object, v4l2object->tv_norm);
for (norms = v4l2object->norms; norms != NULL; norms = norms->next) {
norm = (GstTunerNorm *) norms->data;
- if (!strcmp (norm->label, v4l2object->norm))
+ if (!strcmp (norm->label, current->label))
break;
}
/* If it's possible, set framerate to that (discrete) value */
GList *channels;
/* properties */
- gchar *norm;
+ v4l2_std_id tv_norm;
gchar *channel;
gulong frequency;
PROP_DEVICE, \
PROP_DEVICE_NAME, \
PROP_DEVICE_FD, \
- PROP_FLAGS, \
+ PROP_FLAGS, \
PROP_BRIGHTNESS, \
PROP_CONTRAST, \
PROP_SATURATION, \
- PROP_HUE
+ PROP_HUE, \
+ PROP_TV_NORM
/* create/destroy */
GstV4l2Object * gst_v4l2_object_new (GstElement * element,
#include "gstv4l2colorbalance.h"
+#include "gstv4l2tuner.h"
#ifdef HAVE_XVIDEO
#include "gstv4l2xoverlay.h"
#endif
GST_IMPLEMENT_V4L2_PROBE_METHODS (GstV4l2SinkClass, gst_v4l2sink);
GST_IMPLEMENT_V4L2_COLOR_BALANCE_METHODS (GstV4l2Sink, gst_v4l2sink);
+GST_IMPLEMENT_V4L2_TUNER_METHODS (GstV4l2Sink, gst_v4l2sink);
#ifdef HAVE_XVIDEO
GST_IMPLEMENT_V4L2_XOVERLAY_METHODS (GstV4l2Sink, gst_v4l2sink);
#endif
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);
+ iface_type == GST_TYPE_VIDEO_ORIENTATION || iface_type == GST_TYPE_TUNER);
#else
g_assert (iface_type == GST_TYPE_COLOR_BALANCE ||
- iface_type == GST_TYPE_VIDEO_ORIENTATION);
+ iface_type == GST_TYPE_VIDEO_ORIENTATION || iface_type == GST_TYPE_TUNER);
#endif
if (v4l2object->video_fd == -1)
NULL,
NULL,
};
+ static const GInterfaceInfo v4l2_tuner_info = {
+ (GInterfaceInitFunc) gst_v4l2sink_tuner_interface_init,
+ NULL,
+ NULL,
+ };
#ifdef HAVE_XVIDEO
static const GInterfaceInfo v4l2_xoverlay_info = {
(GInterfaceInitFunc) gst_v4l2sink_xoverlay_interface_init,
g_type_add_interface_static (type,
GST_TYPE_IMPLEMENTS_INTERFACE, &v4l2iface_info);
+ g_type_add_interface_static (type, GST_TYPE_TUNER, &v4l2_tuner_info);
#ifdef HAVE_XVIDEO
g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &v4l2_xoverlay_info);
g_type_add_interface_static (type,
GstTunerNorm *
gst_v4l2_tuner_get_norm (GstV4l2Object * v4l2object)
{
- GList *item;
v4l2_std_id norm;
/* assert that we're opened and that we're using a known item */
gst_v4l2_get_norm (v4l2object, &norm);
+ return gst_v4l2_tuner_get_norm_by_std_id (v4l2object, norm);
+}
+
+GstTunerNorm *
+gst_v4l2_tuner_get_norm_by_std_id (GstV4l2Object * v4l2object, v4l2_std_id norm)
+{
+ GList *item;
+
for (item = v4l2object->norms; item != NULL; item = item->next) {
if (norm & GST_V4L2_TUNER_NORM (item->data)->index)
return (GstTunerNorm *) item->data;
return NULL;
}
+v4l2_std_id
+gst_v4l2_tuner_get_std_id_by_norm (GstV4l2Object * v4l2object,
+ GstTunerNorm * norm)
+{
+ GList *item;
+
+ for (item = v4l2object->norms; item != NULL; item = item->next) {
+ if (norm == GST_TUNER_NORM (item->data))
+ return GST_V4L2_TUNER_NORM (item->data)->index;
+ }
+
+ return 0;
+}
+
void
gst_v4l2_tuner_set_frequency_and_notify (GstV4l2Object * v4l2object,
GstTunerChannel * channel, gulong frequency)
GstTunerNorm* gst_v4l2_tuner_get_norm (GstV4l2Object * v4l2object);
gboolean gst_v4l2_tuner_set_norm (GstV4l2Object * v4l2object,
GstTunerNorm * norm);
+GstTunerNorm* gst_v4l2_tuner_get_norm_by_std_id (GstV4l2Object * v4l2object,
+ v4l2_std_id norm);
+v4l2_std_id gst_v4l2_tuner_get_std_id_by_norm (GstV4l2Object * v4l2object,
+ GstTunerNorm * norm);
+
/* frequency */
void gst_v4l2_tuner_set_frequency_and_notify (GstV4l2Object * v4l2object,
GstTunerChannel * channel,
standard.frameperiod.denominator, standard.frameperiod.numerator);
v4l2norm->index = standard.id;
+ GST_DEBUG_OBJECT (v4l2object->element, "index=%08x, label=%s",
+ (unsigned int) v4l2norm->index, norm->label);
+
v4l2object->norms = g_list_prepend (v4l2object->norms, (gpointer) norm);
}
v4l2object->norms = g_list_reverse (v4l2object->norms);