diff --git a/common b/common index 46dfcea233..69b981f10c 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 46dfcea233cf6df83e3771d8a8066e87d614f893 +Subproject commit 69b981f10caa234ad0ff639179d0fda8505bd94b diff --git a/configure.ac b/configure.ac index 09b435c0bd..77cfe5162d 100644 --- a/configure.ac +++ b/configure.ac @@ -813,15 +813,6 @@ dnl *** libcaca *** 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 *** diff --git a/docs/plugins/gst-plugins-good-plugins.args b/docs/plugins/gst-plugins-good-plugins.args index acbd788a0f..0c96249e11 100644 --- a/docs/plugins/gst-plugins-good-plugins.args +++ b/docs/plugins/gst-plugins-good-plugins.args @@ -461,7 +461,7 @@ GstUDPSrc::sockfd gint ->= G_MAXULONG +>= -1 rw Socket Handle Socket to use for UDP reception. (-1 == allocate). @@ -511,7 +511,7 @@ GstUDPSrc::sock gint ->= G_MAXULONG +>= -1 r Socket Handle Socket currently in use for UDP reception. (-1 = no socket). @@ -1651,7 +1651,7 @@ GstDV1394Src::port gint -[G_MAXULONG,16] +[-1,16] rw Port Port number (-1 automatic). @@ -1901,7 +1901,7 @@ GstTest::allowed-timestamp-deviation gint64 ->= G_MAXULONG +>= -1 rwx allowed timestamp deviation allowed average difference in usec between timestamp of next buffer and expected timestamp from analyzing last buffer. @@ -1911,7 +1911,7 @@ GstTest::buffer-count gint64 ->= G_MAXULONG +>= -1 r buffer count number of buffers in stream. @@ -1921,7 +1921,7 @@ GstTest::expected-buffer-count gint64 ->= G_MAXULONG +>= -1 rwx expected buffer count expected number of buffers in stream. @@ -1931,7 +1931,7 @@ GstTest::expected-length gint64 ->= G_MAXULONG +>= -1 rwx expected length expected length of stream. @@ -1951,7 +1951,7 @@ GstTest::length gint64 ->= G_MAXULONG +>= -1 r length length of stream. @@ -1971,7 +1971,7 @@ GstTest::timestamp-deviation gint64 ->= G_MAXULONG +>= -1 r timestamp deviation average difference in usec between timestamp of next buffer and expected timestamp from analyzing last buffer. @@ -2051,7 +2051,7 @@ GstBreakMyData::set-to gint -[G_MAXULONG,255] +[-1,255] rwx set-to set changed bytes to this value (-1 means random value. @@ -2391,7 +2391,7 @@ GstDynUDPSink::sockfd gint -[G_MAXULONG,32767] +[-1,32767] rw socket handle Socket to use for UDP sending. (-1 == allocate). @@ -2461,7 +2461,7 @@ GstMultiUDPSink::sock gint ->= G_MAXULONG +>= -1 r Socket Handle Socket currently in use for UDP sending. (-1 == no socket). @@ -2471,7 +2471,7 @@ GstMultiUDPSink::sockfd gint ->= G_MAXULONG +>= -1 rw Socket Handle Socket to use for UDP sending. (-1 == allocate). @@ -2501,7 +2501,7 @@ GstMultiUDPSink::qos-dscp gint -[G_MAXULONG,63] +[-1,63] rw QoS diff srv code point Quality of Service, differentiated services code point (-1 default). @@ -2651,7 +2651,7 @@ GstXImageSrc::screen-num guint -<= G_MAXINT +<= G_MAXLONG rw Screen number X Screen Number. @@ -2671,7 +2671,7 @@ GstXImageSrc::endx guint -<= G_MAXINT +<= G_MAXLONG rw End X X coordinate of bottom right corner of area to be recorded (0 for bottom right of screen). @@ -2681,7 +2681,7 @@ GstXImageSrc::endy guint -<= G_MAXINT +<= G_MAXLONG rw End Y Y coordinate of bottom right corner of area to be recorded (0 for bottom right of screen). @@ -2691,7 +2691,7 @@ GstXImageSrc::startx guint -<= G_MAXINT +<= G_MAXLONG rw Start X co-ordinate X coordinate of top left corner of area to be recorded (0 for top left of screen). @@ -2701,7 +2701,7 @@ GstXImageSrc::starty guint -<= G_MAXINT +<= G_MAXLONG rw Start Y co-ordinate Y coordinate of top left corner of area to be recorded (0 for top left of screen). @@ -2811,7 +2811,7 @@ GstJpegDec::max-errors gint ->= G_MAXULONG +>= -1 rw Maximum Consecutive Decoding Errors Error out after receiving N consecutive decoding errors (-1 = never fail, 0 = automatic, 1 = fail on first error). @@ -3121,7 +3121,7 @@ GstV4l2Src::device-fd gint ->= G_MAXULONG +>= -1 r File descriptor File descriptor of the device. @@ -3391,7 +3391,7 @@ GstRndBufferSize::max glong -[1,G_MAXINT] +>= 1 rwx maximum maximum buffer size. @@ -3401,7 +3401,7 @@ GstRndBufferSize::min glong -[0,G_MAXINT] +>= 0 rwx mininum mininum buffer size. @@ -3411,7 +3411,7 @@ GstRndBufferSize::seed gulong -<= G_MAXUINT + rwx random number seed seed for randomness (initialized when going from READY to PAUSED). @@ -20111,7 +20111,7 @@ GstHDV1394Src::port gint -[G_MAXULONG,16] +[-1,16] rw Port Port number (-1 automatic). @@ -20398,6 +20398,36 @@ Auto detection + +GstDeinterlace::drop-orphans +gboolean + +rw +drop-orphans +Drop orphan fields at the beginning of telecine patterns in active locking mode. +TRUE + + + +GstDeinterlace::ignore-obscure +gboolean + +rw +ignore-obscure +Ignore obscure telecine patterns (only consider P, I and 2:3 variants). +TRUE + + + +GstDeinterlace::locking +GstDeinterlaceLocking + +rw +locking +Pattern locking mode. +No pattern locking + + GstAgingTV::color-aging gboolean @@ -20461,7 +20491,7 @@ GstOpTV::threshold guint -<= G_MAXINT +<= G_MAXLONG rw Threshold Luma threshold. @@ -20481,7 +20511,7 @@ GstRadioacTV::interval guint -<= G_MAXINT +<= G_MAXLONG rw Interval Snapshot interval (in strobe mode). @@ -20751,7 +20781,7 @@ GstRtpSession::rtcp-rr-bandwidth gint ->= G_MAXULONG +>= -1 rw RTCP RR bandwidth The RTCP bandwidth used for receivers in bytes per second (-1 = default). @@ -20761,7 +20791,7 @@ GstRtpSession::rtcp-rs-bandwidth gint ->= G_MAXULONG +>= -1 rw RTCP RS bandwidth The RTCP bandwidth used for senders in bytes per second (-1 = default). @@ -20801,7 +20831,7 @@ GstV4l2Sink::device-fd gint ->= G_MAXULONG +>= -1 r File descriptor File descriptor of the device. diff --git a/docs/plugins/gst-plugins-good-plugins.hierarchy b/docs/plugins/gst-plugins-good-plugins.hierarchy index 33aceea49a..5e1dff3865 100644 --- a/docs/plugins/gst-plugins-good-plugins.hierarchy +++ b/docs/plugins/gst-plugins-good-plugins.hierarchy @@ -1,315 +1,319 @@ 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 + 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 + GstCmmlDec + GstCmmlEnc + GstCutter + GstDVDec + GstDVDemux + GstDeinterlace + GstDeinterleave + GstEFence + GstFlacDec + GstFlacEnc + GstFlacTag + GstFlvDemux + GstFlvMux + GstFlxDec + GstGPPMux + GstGdkPixbuf GstGoom GstGoom2k1 - GstWavEnc + GstICYDemux + GstISMLMux + GstImageFreeze + GstInterleave + GstJpegDec + GstJpegEnc + GstMJ2Mux + GstMP4Mux + GstMatroskaDemux + GstMatroskaMux + GstWebMMux + GstMatroskaParse + GstMonoscope + GstMuLawDec + GstMuLawEnc + GstMultipartDemux + GstMultipartMux + GstOss4Mixer + GstOssMixerElement + GstPngDec + GstPngEnc + GstPulseMixer + GstQTDemux + GstQTMux + GstRTPDec + GstRTPDepay + GstRndBufferSize GstRtpJitterBuffer GstRtpPtDemux GstRtpSession GstRtpSsrcDemux - GstRndBufferSize - GstCapsDebug - GstEFence - GstCutter - GstMatroskaDemux - GstMatroskaParse - GstMatroskaMux - GstWebMMux - GstRTPDec GstSMPTE - GstAuParse - GstMultipartDemux - GstMultipartMux - GstALawEnc - GstALawDec - GstMuLawEnc - GstMuLawDec + 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 - GstTuner - GstColorBalance - GstVideoOrientation + GstNavigation + GstPreset + GstPropertyProbe + GstStreamVolume + GstTagSetter GstTagXmpWriter - GIcon + GstTuner + GstURIHandler + GstVideoOrientation + GstXOverlay diff --git a/docs/plugins/gst-plugins-good-plugins.interfaces b/docs/plugins/gst-plugins-good-plugins.interfaces index 8cb7022d8d..518366a8ab 100644 --- a/docs/plugins/gst-plugins-good-plugins.interfaces +++ b/docs/plugins/gst-plugins-good-plugins.interfaces @@ -1,62 +1,63 @@ -GstBin GstChildProxy -GstPipeline GstChildProxy -GstQTMoovRecover GstChildProxy -GstSwitchSink GstChildProxy -GstGConfVideoSink GstChildProxy -GstGConfAudioSink GstChildProxy -GstSwitchSrc GstChildProxy -GstGConfVideoSrc GstChildProxy -GstGConfAudioSrc GstChildProxy -GstHalAudioSink GstChildProxy -GstHalAudioSrc GstChildProxy -GstRtpBin GstChildProxy -GstAutoVideoSink GstChildProxy -GstAutoVideoSrc GstChildProxy +GdkPixbuf GIcon +Gst3GPPMux GstTagSetter GstTagXmpWriter +GstApev2Mux GstTagSetter +GstAspectRatioCrop GstChildProxy GstAutoAudioSink GstChildProxy GstAutoAudioSrc GstChildProxy -GstPushFileSrc GstChildProxy GstURIHandler -GstRTSPSrc GstChildProxy GstURIHandler -GstRgVolume GstChildProxy -GstAspectRatioCrop GstChildProxy -GstPulseSink GstStreamVolume GstImplementsInterface GstPropertyProbe -GstOss4Sink GstStreamVolume GstPropertyProbe -GstShout2send GstTagSetter -GstUDPSink GstURIHandler +GstAutoVideoSink GstChildProxy +GstAutoVideoSrc GstChildProxy +GstAviMux GstTagSetter +GstBin GstChildProxy 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 +GstDeinterlace GstChildProxy 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 -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 +GstGConfAudioSink GstChildProxy +GstGConfAudioSrc GstChildProxy +GstGConfVideoSink GstChildProxy +GstGConfVideoSrc GstChildProxy GstGPPMux GstTagSetter GstTagXmpWriter +GstHDV1394Src GstURIHandler GstPropertyProbe +GstHalAudioSink GstChildProxy +GstHalAudioSrc 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 +GstRtpBin GstChildProxy +GstShout2send GstTagSetter +GstSoupHTTPSrc GstURIHandler +GstSpeexEnc GstTagSetter GstPreset +GstSwitchSink GstChildProxy +GstSwitchSrc GstChildProxy +GstTagLibMux GstTagSetter +GstUDPSink GstURIHandler +GstUDPSrc GstURIHandler +GstV4l2Radio GstURIHandler GstImplementsInterface GstTuner GstPropertyProbe +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 diff --git a/docs/plugins/gst-plugins-good-plugins.prerequisites b/docs/plugins/gst-plugins-good-plugins.prerequisites index ec5b5e29be..5c212bb7a4 100644 --- a/docs/plugins/gst-plugins-good-plugins.prerequisites +++ b/docs/plugins/gst-plugins-good-plugins.prerequisites @@ -1,10 +1,11 @@ +GIcon GObject GstChildProxy GstObject -GstTagSetter GstElement -GstStreamVolume GObject +GstColorBalance GstImplementsInterface GstElement GstImplementsInterface GstElement GstMixer GstImplementsInterface GstElement -GstTuner GstImplementsInterface GstElement -GstColorBalance GstImplementsInterface GstElement -GstVideoOrientation GstImplementsInterface GstElement +GstStreamVolume GObject +GstTagSetter GstElement GstTagXmpWriter GstElement -GIcon GObject +GstTuner GstImplementsInterface GstElement +GstVideoOrientation GstImplementsInterface GstElement +GstXOverlay GstImplementsInterface GstElement diff --git a/ext/flac/gstflacdec.c b/ext/flac/gstflacdec.c index 6eaee89781..0f8a6758bb 100644 --- a/ext/flac/gstflacdec.c +++ b/ext/flac/gstflacdec.c @@ -1732,8 +1732,9 @@ gst_flac_dec_src_query (GstPad * pad, GstQuery * query) 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)); diff --git a/gst/audioparsers/gstflacparse.c b/gst/audioparsers/gstflacparse.c index 0249e88a2d..0c6c529875 100644 --- a/gst/audioparsers/gstflacparse.c +++ b/gst/audioparsers/gstflacparse.c @@ -198,6 +198,9 @@ static GstFlowReturn gst_flac_parse_parse_frame (GstBaseParse * parse, 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); @@ -244,6 +247,7 @@ gst_flac_parse_class_init (GstFlacParseClass * klass) 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 @@ -756,17 +760,15 @@ gst_flac_parse_handle_streaminfo (GstFlacParse * flacparse, GstBuffer * buffer) 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)) @@ -796,10 +798,10 @@ gst_flac_parse_handle_streaminfo (GstFlacParse * flacparse, GstBuffer * buffer) 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" @@ -1353,3 +1355,35 @@ gst_flac_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) 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); +} diff --git a/gst/auparse/gstauparse.c b/gst/auparse/gstauparse.c index 715acb2c2d..69e5273192 100644 --- a/gst/auparse/gstauparse.c +++ b/gst/auparse/gstauparse.c @@ -78,6 +78,9 @@ static gboolean gst_au_parse_add_srcpad (GstAuParse * auparse, GstCaps * caps); 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); @@ -251,7 +254,9 @@ gst_au_parse_parse_header (GstAuParse * auparse) } 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); @@ -425,6 +430,9 @@ gst_au_parse_chain (GstPad * pad, GstBuffer * buf) 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)); @@ -446,7 +454,7 @@ gst_au_parse_chain (GstPad * pad, GstBuffer * buf) 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)); } @@ -464,6 +472,7 @@ gst_au_parse_chain (GstPad * pad, GstBuffer * buf) 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), @@ -478,6 +487,22 @@ gst_au_parse_chain (GstPad * pad, GstBuffer * buf) 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); @@ -517,6 +542,9 @@ gst_au_parse_src_convert (GstAuParse * auparse, GstFormat src_format, /* fallthrough */ case GST_FORMAT_DEFAULT:{ switch (dest_format) { + case GST_FORMAT_DEFAULT: + *destval = srcval; + break; case GST_FORMAT_BYTES: *destval = srcval * samplesize; break; @@ -532,8 +560,8 @@ gst_au_parse_src_convert (GstAuParse * auparse, GstFormat src_format, 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); @@ -581,8 +609,7 @@ gst_au_parse_src_query (GstPad * pad, GstQuery * query) 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); @@ -611,6 +638,17 @@ gst_au_parse_src_query (GstPad * pad, GstQuery * query) } 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; @@ -628,6 +666,7 @@ gst_au_parse_handle_seek (GstAuParse * auparse, GstEvent * event) GstFormat format; gdouble rate; gint64 start, stop; + gboolean res; gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, &stop_type, &stop); @@ -637,19 +676,79 @@ gst_au_parse_handle_seek (GstAuParse * auparse, GstEvent * event) 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; diff --git a/gst/avi/gstavidemux.c b/gst/avi/gstavidemux.c index 845ccf84e5..def2b93f55 100644 --- a/gst/avi/gstavidemux.c +++ b/gst/avi/gstavidemux.c @@ -1881,6 +1881,8 @@ gst_avi_demux_roundup_list (GstAviDemux * avi, GstBuffer ** buf) 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); } diff --git a/gst/debugutils/rndbuffersize.c b/gst/debugutils/rndbuffersize.c index a9ed67fb6f..41934905d7 100644 --- a/gst/debugutils/rndbuffersize.c +++ b/gst/debugutils/rndbuffersize.c @@ -113,7 +113,7 @@ gst_rnd_buffer_size_base_init (gpointer g_class) gst_element_class_set_details_simple (gstelement_class, "Random buffer size", "Testing", "pull random sized buffers", - "Stefan Kost )"); + "Stefan Kost "); } diff --git a/gst/deinterlace/gstdeinterlace.c b/gst/deinterlace/gstdeinterlace.c index ce76f720c8..415cb00691 100644 --- a/gst/deinterlace/gstdeinterlace.c +++ b/gst/deinterlace/gstdeinterlace.c @@ -2,6 +2,7 @@ * GStreamer * Copyright (C) 2005 Martin Eikermann * Copyright (C) 2008-2010 Sebastian Dröge + * Copyright (C) 2011 Robert Swain * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -55,6 +56,9 @@ GST_DEBUG_CATEGORY_STATIC (deinterlace_debug); #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 { @@ -63,9 +67,73 @@ 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"}, @@ -85,6 +153,21 @@ static const GEnumValue methods_types[] = { {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 @@ -160,6 +243,21 @@ gst_deinterlace_modes_get_type (void) 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 ";" \ @@ -504,6 +602,51 @@ gst_deinterlace_class_init (GstDeinterlaceClass * klass) 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); } @@ -571,7 +714,17 @@ gst_deinterlace_init (GstDeinterlace * self, GstDeinterlaceClass * klass) 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); @@ -606,6 +759,12 @@ gst_deinterlace_reset_history (GstDeinterlace * self, gboolean drop_all) 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); @@ -650,6 +809,9 @@ gst_deinterlace_reset (GstDeinterlace * self) gst_deinterlace_reset_history (self, TRUE); gst_deinterlace_reset_qos (self); + + self->need_more = FALSE; + self->have_eos = FALSE; } static void @@ -698,6 +860,15 @@ gst_deinterlace_set_property (GObject * object, guint prop_id, 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); } @@ -726,6 +897,15 @@ gst_deinterlace_get_property (GObject * object, guint prop_id, 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); } @@ -746,6 +926,32 @@ gst_deinterlace_finalize (GObject * object) 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) { @@ -759,6 +965,21 @@ 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, @@ -768,6 +989,78 @@ gst_deinterlace_pop_history (GstDeinterlace * self) 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) { @@ -781,14 +1074,47 @@ 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; @@ -821,12 +1147,16 @@ gst_deinterlace_push_history (GstDeinterlace * self, GstBuffer * buffer) 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; @@ -943,84 +1273,553 @@ gst_deinterlace_do_qos (GstDeinterlace * self, GstClockTime timestamp) 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; - gst_deinterlace_set_method (self, self->user_set_method_id); - fields_required = gst_deinterlace_method_get_fields_required (self->method); +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 < 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); + if (!self->history_count) { + GST_DEBUG_OBJECT (self, "History is empty, waiting for more buffers!"); + goto need_more; + } + + 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; + } + + GST_DEBUG_OBJECT (self, + "Frame type: Interlaced; deinterlacing using %s method", + methods_types[self->method_id].value_nick); + } else { + GstBuffer *field1_buf; + + /* progressive */ + fields_required = 2; + + /* 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; + } + + 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_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); + } + + 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; + + if (interlacing_method != GST_DEINTERLACE_TELECINE) { + timestamp = GST_BUFFER_TIMESTAMP (buf); + + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + if (self->fields == GST_DEINTERLACE_ALL) + 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); + + gst_buffer_unref (gst_deinterlace_pop_history (self)); + + 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; + 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 (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; } } - 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->history_count < fields_required) + return ret; - cur_field_idx = self->history_count - fields_required; + /* 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"); - 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; - /* 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); - 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); - gst_buffer_unref (outbuf); - outbuf = gst_buffer_try_new_and_alloc (self->frame_size); + if (!outbuf) + return GST_FLOW_ERROR; - if (!outbuf) - return GST_FLOW_ERROR; + gst_buffer_set_caps (outbuf, GST_PAD_CAPS (self->srcpad)); + } - 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; @@ -1028,127 +1827,111 @@ gst_deinterlace_output_frame (GstDeinterlace * self, gboolean flushing) 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 (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; - timestamp = GST_BUFFER_TIMESTAMP (buf); - - GST_BUFFER_TIMESTAMP (outbuf) = timestamp; - if (self->fields == GST_DEINTERLACE_ALL) - 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))) { + 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)); - 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; + 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) { @@ -1166,8 +1949,23 @@ gst_deinterlace_chain (GstPad * pad, GstBuffer * buf) 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"); @@ -1177,7 +1975,11 @@ gst_deinterlace_chain (GstPad * pad, GstBuffer * buf) 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 @@ -1386,6 +2188,23 @@ gst_deinterlace_setcaps (GstPad * pad, GstCaps * caps) 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, @@ -1398,27 +2217,51 @@ gst_deinterlace_setcaps (GstPad * pad, GstCaps * caps) 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); + 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; - gst_caps_set_simple (srccaps, "framerate", GST_TYPE_FRACTION, fps_n, - fps_d, NULL); + 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; @@ -1475,8 +2318,8 @@ gst_deinterlace_sink_event (GstPad * pad, GstEvent * event) 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); @@ -1524,6 +2367,7 @@ gst_deinterlace_sink_event (GstPad * pad, GstEvent * event) } /* fall through */ case GST_EVENT_EOS: + self->have_eos = TRUE; gst_deinterlace_reset_history (self, FALSE); /* fall through */ diff --git a/gst/deinterlace/gstdeinterlace.h b/gst/deinterlace/gstdeinterlace.h index df996f8425..ac97a555ec 100644 --- a/gst/deinterlace/gstdeinterlace.h +++ b/gst/deinterlace/gstdeinterlace.h @@ -43,8 +43,6 @@ G_BEGIN_DECLS typedef struct _GstDeinterlace GstDeinterlace; typedef struct _GstDeinterlaceClass GstDeinterlaceClass; -#define GST_DEINTERLACE_MAX_FIELD_HISTORY 10 - typedef enum { GST_DEINTERLACE_TOMSMOCOMP, @@ -79,6 +77,39 @@ typedef enum { 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; @@ -92,8 +123,10 @@ struct _GstDeinterlace 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; @@ -134,6 +167,24 @@ struct _GstDeinterlace 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 diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c index 42056fb44f..f29015e714 100644 --- a/gst/isomp4/qtdemux.c +++ b/gst/isomp4/qtdemux.c @@ -2723,6 +2723,7 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux) ("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; } @@ -9320,6 +9321,7 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, 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", diff --git a/gst/matroska/Makefile.am b/gst/matroska/Makefile.am index 195c68057a..7d569737b3 100644 --- a/gst/matroska/Makefile.am +++ b/gst/matroska/Makefile.am @@ -8,6 +8,7 @@ libgstmatroska_la_SOURCES = \ matroska-parse.c \ matroska-ids.c \ matroska-mux.c \ + matroska-read-common.c \ webm-mux.c \ lzo.c @@ -19,6 +20,7 @@ noinst_HEADERS = \ matroska-parse.h \ matroska-ids.h \ matroska-mux.h \ + matroska-read-common.h \ webm-mux.h \ lzo.h diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 788c8dd689..02a0ffcd77 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -2,6 +2,7 @@ * (c) 2003 Ronald Bultje * (c) 2006 Tim-Philipp Müller * (c) 2008 Sebastian Dröge + * (c) 2011 Debarshi Ray * * matroska-demux.c: matroska file/stream demuxer * @@ -62,18 +63,8 @@ #include -#ifdef HAVE_ZLIB -#include -#endif - -#ifdef HAVE_BZ2 -#include -#endif - #include -#include "lzo.h" - #include "matroska-demux.h" #include "matroska-ids.h" @@ -208,9 +199,9 @@ gst_matroska_demux_finalize (GObject * object) { 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) { @@ -263,11 +254,11 @@ gst_matroska_demux_init (GstMatroskaDemux * demux, 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 (); @@ -325,9 +316,10 @@ gst_matroska_demux_combine_flows (GstMatroskaDemux * demux, 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; @@ -360,13 +352,14 @@ gst_matroska_demux_reset (GstElement * element) 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); @@ -374,11 +367,11 @@ gst_matroska_demux_reset (GstElement * element) 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; @@ -390,9 +383,9 @@ gst_matroska_demux_reset (GstElement * element) 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) { @@ -402,10 +395,10 @@ gst_matroska_demux_reset (GstElement * element) /* 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; @@ -450,11 +443,11 @@ gst_matroska_demux_reset (GstElement * element) 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); @@ -583,27 +576,6 @@ gst_matroska_demux_get_length (GstMatroskaDemux * demux) return end; } -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) @@ -793,176 +765,6 @@ gst_matroska_demux_read_track_encoding (GstMatroskaDemux * demux, return ret; } -static gboolean -gst_matroska_decompress_data (GstMatroskaTrackEncoding * enc, - guint8 ** data_out, guint * size_out, - GstMatroskaTrackCompressionAlgorithm algo) -{ - guint8 *new_data = NULL; - guint new_size = 0; - guint8 *data = *data_out; - guint size = *size_out; - gboolean ret = TRUE; - - if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_ZLIB) { -#ifdef HAVE_ZLIB - /* zlib encoded data */ - z_stream zstream; - guint orig_size; - int result; - - orig_size = size; - zstream.zalloc = (alloc_func) 0; - zstream.zfree = (free_func) 0; - zstream.opaque = (voidpf) 0; - if (inflateInit (&zstream) != Z_OK) { - GST_WARNING ("zlib initialization failed."); - ret = FALSE; - goto out; - } - zstream.next_in = (Bytef *) data; - zstream.avail_in = orig_size; - new_size = orig_size; - new_data = g_malloc (new_size); - zstream.avail_out = new_size; - zstream.next_out = (Bytef *) new_data; - - do { - result = inflate (&zstream, Z_NO_FLUSH); - if (result != Z_OK && result != Z_STREAM_END) { - GST_WARNING ("zlib decompression failed."); - g_free (new_data); - inflateEnd (&zstream); - break; - } - new_size += 4000; - new_data = g_realloc (new_data, new_size); - zstream.next_out = (Bytef *) (new_data + zstream.total_out); - zstream.avail_out += 4000; - } while (zstream.avail_in != 0 && result != Z_STREAM_END); - - if (result != Z_STREAM_END) { - ret = FALSE; - goto out; - } else { - new_size = zstream.total_out; - inflateEnd (&zstream); - } -#else - GST_WARNING ("zlib encoded tracks not supported."); - ret = FALSE; - goto out; -#endif - } else if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_BZLIB) { -#ifdef HAVE_BZ2 - /* bzip2 encoded data */ - bz_stream bzstream; - guint orig_size; - int result; - - bzstream.bzalloc = NULL; - bzstream.bzfree = NULL; - bzstream.opaque = NULL; - orig_size = size; - - if (BZ2_bzDecompressInit (&bzstream, 0, 0) != BZ_OK) { - GST_WARNING ("bzip2 initialization failed."); - ret = FALSE; - goto out; - } - - bzstream.next_in = (char *) data; - bzstream.avail_in = orig_size; - new_size = orig_size; - new_data = g_malloc (new_size); - bzstream.avail_out = new_size; - bzstream.next_out = (char *) new_data; - - do { - result = BZ2_bzDecompress (&bzstream); - if (result != BZ_OK && result != BZ_STREAM_END) { - GST_WARNING ("bzip2 decompression failed."); - g_free (new_data); - BZ2_bzDecompressEnd (&bzstream); - break; - } - new_size += 4000; - new_data = g_realloc (new_data, new_size); - bzstream.next_out = (char *) (new_data + bzstream.total_out_lo32); - bzstream.avail_out += 4000; - } while (bzstream.avail_in != 0 && result != BZ_STREAM_END); - - if (result != BZ_STREAM_END) { - ret = FALSE; - goto out; - } else { - new_size = bzstream.total_out_lo32; - BZ2_bzDecompressEnd (&bzstream); - } -#else - GST_WARNING ("bzip2 encoded tracks not supported."); - ret = FALSE; - goto out; -#endif - } else if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_LZO1X) { - /* lzo encoded data */ - int result; - int orig_size, out_size; - - orig_size = size; - out_size = size; - new_size = size; - new_data = g_malloc (new_size); - - do { - orig_size = size; - out_size = new_size; - - result = lzo1x_decode (new_data, &out_size, data, &orig_size); - - if (orig_size > 0) { - new_size += 4000; - new_data = g_realloc (new_data, new_size); - } - } while (orig_size > 0 && result == LZO_OUTPUT_FULL); - - new_size -= out_size; - - if (result != LZO_OUTPUT_FULL) { - GST_WARNING ("lzo decompression failed"); - g_free (new_data); - - ret = FALSE; - goto out; - } - - } else if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_HEADERSTRIP) { - /* header stripped encoded data */ - if (enc->comp_settings_length > 0) { - new_data = g_malloc (size + enc->comp_settings_length); - new_size = size + enc->comp_settings_length; - - memcpy (new_data, enc->comp_settings, enc->comp_settings_length); - memcpy (new_data + enc->comp_settings_length, data, size); - } - } else { - GST_ERROR ("invalid compression algorithm %d", algo); - ret = FALSE; - } - -out: - - if (!ret) { - *data_out = NULL; - *size_out = 0; - } else { - *data_out = new_data; - *size_out = new_size; - } - - return ret; -} - static gboolean gst_matroska_decode_data (GArray * encodings, guint8 ** data_out, guint * size_out, GstMatroskaTrackEncodingScope scope, gboolean free) @@ -1059,49 +861,6 @@ gst_matroska_decode_buffer (GstMatroskaTrackContext * context, GstBuffer * buf) } } -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) @@ -1151,9 +910,10 @@ gst_matroska_demux_tracknumber_unique (GstMatroskaDemux * demux, guint64 num) { 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; @@ -1187,8 +947,8 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) /* 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; @@ -1200,8 +960,8 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) 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); @@ -1293,7 +1053,8 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) 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; } @@ -1312,7 +1073,8 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) 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)) { @@ -1532,7 +1294,8 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) 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)) { @@ -1832,9 +1595,9 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) 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); } @@ -2127,14 +1890,14 @@ gst_matroskademux_do_index_seek (GstMatroskaDemux * demux, 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, @@ -2187,11 +1950,11 @@ gst_matroska_demux_send_event (GstMatroskaDemux * demux, GstEvent * event) 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; @@ -2249,10 +2012,10 @@ gst_matroska_demux_get_seek_track (GstMatroskaDemux * demux, 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; } @@ -2269,9 +2032,10 @@ gst_matroska_demux_reset_streams (GstMatroskaDemux * demux, GstClockTime time, 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; @@ -2297,10 +2061,10 @@ gst_matroska_demux_move_to_entry (GstMatroskaDemux * demux, /* 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 */ @@ -2310,8 +2074,8 @@ gst_matroska_demux_move_to_entry (GstMatroskaDemux * demux, 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; @@ -2458,7 +2222,7 @@ static GstMatroskaIndex * 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; @@ -2476,18 +2240,18 @@ gst_matroska_demux_search_pos (GstMatroskaDemux * demux, GstClockTime time) 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); @@ -2500,7 +2264,7 @@ retry: 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, @@ -2558,7 +2322,7 @@ retry: } 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, @@ -2609,7 +2373,7 @@ retry: 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); @@ -2621,7 +2385,7 @@ exit: 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; } @@ -2673,7 +2437,7 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux, 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; @@ -2687,7 +2451,7 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux, /* 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); @@ -2709,7 +2473,7 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux, 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 ()); @@ -2840,7 +2604,7 @@ gst_matroska_demux_handle_seek_push (GstMatroskaDemux * demux, GstPad * pad, } /* check for having parsed index already */ - if (!demux->index_parsed) { + if (!demux->common.index_parsed) { gboolean building_index; guint64 offset = 0; @@ -2851,7 +2615,7 @@ gst_matroska_demux_handle_seek_push (GstMatroskaDemux * demux, GstPad * pad, 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 */ @@ -2891,7 +2655,7 @@ gst_matroska_demux_handle_src_event (GstPad * pad, GstEvent * 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; } @@ -2958,8 +2722,8 @@ gst_matroska_demux_seek_to_previous_keyframe (GstMatroskaDemux * demux) 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, @@ -2993,23 +2757,6 @@ exit: 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) { @@ -3121,7 +2868,8 @@ 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; @@ -3190,7 +2938,8 @@ gst_matroska_demux_parse_tracks (GstMatroskaDemux * demux, GstEbmlRead * ebml) 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; } } @@ -3201,306 +2950,6 @@ gst_matroska_demux_parse_tracks (GstMatroskaDemux * demux, GstEbmlRead * ebml) return ret; } -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) { @@ -3529,7 +2978,7 @@ gst_matroska_demux_parse_info (GstMatroskaDemux * demux, GstEbmlRead * ebml) GST_DEBUG_OBJECT (demux, "TimeCodeScale: %" G_GUINT64_FORMAT, num); - demux->time_scale = num; + demux->common.time_scale = num; break; } @@ -3596,7 +3045,8 @@ gst_matroska_demux_parse_info (GstMatroskaDemux * demux, GstEbmlRead * ebml) } 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 */ @@ -3617,7 +3067,7 @@ gst_matroska_demux_parse_info (GstMatroskaDemux * demux, GstEbmlRead * ebml) 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); } @@ -3692,7 +3142,8 @@ gst_matroska_demux_parse_metadata_id_simple_tag (GstMatroskaDemux * demux, 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 */ @@ -3778,7 +3229,8 @@ gst_matroska_demux_parse_metadata_id_tag (GstMatroskaDemux * demux, 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; } } @@ -3834,7 +3286,8 @@ gst_matroska_demux_parse_metadata (GstMatroskaDemux * demux, GstEbmlRead * ebml) 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: @@ -3917,7 +3370,8 @@ gst_matroska_demux_parse_attached_file (GstMatroskaDemux * demux, 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); @@ -4030,7 +3484,8 @@ gst_matroska_demux_parse_attachments (GstMatroskaDemux * demux, 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; } } @@ -4157,11 +3612,11 @@ gst_matroska_demux_sync_streams (GstMatroskaDemux * demux) 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, @@ -4769,14 +4224,15 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, 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 @@ -4784,7 +4240,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, 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)); @@ -4944,7 +4400,8 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, } 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: @@ -4971,7 +4428,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, 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? @@ -4980,11 +4437,11 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, 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; @@ -5008,11 +4465,12 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, 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; @@ -5214,16 +4672,16 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, 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, @@ -5360,7 +4818,8 @@ gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux, } default: - ret = gst_matroska_demux_parse_skip (demux, ebml, "SeekHead", id); + ret = gst_matroska_read_common_parse_skip (&demux->common, ebml, + "SeekHead", id); break; } } @@ -5398,18 +4857,19 @@ gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux, } /* 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); } @@ -5417,7 +4877,7 @@ gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux, } /* 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, @@ -5427,7 +4887,7 @@ gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux, 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); @@ -5441,7 +4901,7 @@ gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux, 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)) @@ -5489,7 +4949,8 @@ gst_matroska_demux_parse_contents (GstMatroskaDemux * demux, GstEbmlRead * ebml) } default: - ret = gst_matroska_demux_parse_skip (demux, ebml, "SeekHead", id); + ret = gst_matroska_read_common_parse_skip (&demux->common, + ebml, "SeekHead", id); break; } } @@ -5731,15 +5192,15 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, 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: @@ -5747,7 +5208,7 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, break; } break; - case GST_MATROSKA_DEMUX_STATE_SEGMENT: + case GST_MATROSKA_READ_STATE_SEGMENT: switch (id) { case GST_MATROSKA_ID_SEGMENT: /* eat segment prefix */ @@ -5757,8 +5218,8 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, 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, @@ -5768,14 +5229,14 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, 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) { @@ -5804,8 +5265,9 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, 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)); @@ -5840,16 +5302,17 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, 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); } @@ -5896,15 +5359,15 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, 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); @@ -5918,7 +5381,7 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, 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; @@ -5997,7 +5460,7 @@ gst_matroska_demux_loop (GstPad * pad) 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; @@ -6029,12 +5492,13 @@ gst_matroska_demux_loop (GstPad * pad) 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) @@ -6234,7 +5698,7 @@ gst_matroska_demux_handle_sink_event (GstPad * pad, GstEvent * event) "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; } @@ -6270,11 +5734,11 @@ gst_matroska_demux_handle_sink_event (GstPad * pad, GstEvent * event) } 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 { @@ -7093,11 +6557,12 @@ gst_matroska_demux_set_index (GstElement * element, GstIndex * index) 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 * @@ -7107,8 +6572,8 @@ gst_matroska_demux_get_index (GstElement * element) 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); diff --git a/gst/matroska/matroska-demux.h b/gst/matroska/matroska-demux.h index a35a5935ac..d0df0ac939 100644 --- a/gst/matroska/matroska-demux.h +++ b/gst/matroska/matroska-demux.h @@ -1,5 +1,6 @@ /* GStreamer Matroska muxer/demuxer * (c) 2003 Ronald Bultje + * (c) 2011 Debarshi Ray * * matroska-demux.h: matroska file/stream demuxer definition * @@ -27,6 +28,7 @@ #include "ebml-read.h" #include "matroska-ids.h" +#include "matroska-read-common.h" G_BEGIN_DECLS @@ -41,28 +43,16 @@ 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; @@ -74,30 +64,20 @@ typedef struct _GstMatroskaDemux { /* 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; diff --git a/gst/matroska/matroska-parse.c b/gst/matroska/matroska-parse.c index 7d488494cf..1b4f724de7 100644 --- a/gst/matroska/matroska-parse.c +++ b/gst/matroska/matroska-parse.c @@ -2,6 +2,7 @@ * (c) 2003 Ronald Bultje * (c) 2006 Tim-Philipp Müller * (c) 2008 Sebastian Dröge + * (c) 2011 Debarshi Ray * * matroska-parse.c: matroska file/stream parser * @@ -62,18 +63,8 @@ #include -#ifdef HAVE_ZLIB -#include -#endif - -#ifdef HAVE_BZ2 -#include -#endif - #include -#include "lzo.h" - #include "matroska-parse.h" #include "matroska-ids.h" @@ -170,9 +161,9 @@ gst_matroska_parse_finalize (GObject * object) { GstMatroskaParse *parse = GST_MATROSKA_PARSE (object); - if (parse->src) { - g_ptr_array_free (parse->src, TRUE); - parse->src = NULL; + if (parse->common.src) { + g_ptr_array_free (parse->common.src, TRUE); + parse->common.src = NULL; } if (parse->global_tags) { @@ -232,11 +223,11 @@ gst_matroska_parse_init (GstMatroskaParse * parse, 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 (); @@ -292,22 +283,23 @@ gst_matroska_parse_reset (GstElement * element) 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; @@ -319,17 +311,17 @@ gst_matroska_parse_reset (GstElement * element) 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; @@ -374,11 +366,11 @@ gst_matroska_parse_reset (GstElement * element) 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); @@ -507,27 +499,6 @@ gst_matroska_parse_get_length (GstMatroskaParse * parse) return end; } -static gint -gst_matroska_parse_stream_from_num (GstMatroskaParse * parse, guint track_num) -{ - guint n; - - g_assert (parse->src->len == parse->num_streams); - for (n = 0; n < parse->src->len; n++) { - GstMatroskaTrackContext *context = g_ptr_array_index (parse->src, n); - - if (context->num == track_num) { - return n; - } - } - - if (n == parse->num_streams) - GST_WARNING_OBJECT (parse, - "Failed to find corresponding pad for tracknum %d", track_num); - - return -1; -} - static gint gst_matroska_parse_encoding_cmp (GstMatroskaTrackEncoding * a, GstMatroskaTrackEncoding * b) @@ -717,176 +688,6 @@ gst_matroska_parse_read_track_encoding (GstMatroskaParse * parse, return ret; } -static gboolean -gst_matroska_decompress_data (GstMatroskaTrackEncoding * enc, - guint8 ** data_out, guint * size_out, - GstMatroskaTrackCompressionAlgorithm algo) -{ - guint8 *new_data = NULL; - guint new_size = 0; - guint8 *data = *data_out; - guint size = *size_out; - gboolean ret = TRUE; - - if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_ZLIB) { -#ifdef HAVE_ZLIB - /* zlib encoded data */ - z_stream zstream; - guint orig_size; - int result; - - orig_size = size; - zstream.zalloc = (alloc_func) 0; - zstream.zfree = (free_func) 0; - zstream.opaque = (voidpf) 0; - if (inflateInit (&zstream) != Z_OK) { - GST_WARNING ("zlib initialization failed."); - ret = FALSE; - goto out; - } - zstream.next_in = (Bytef *) data; - zstream.avail_in = orig_size; - new_size = orig_size; - new_data = g_malloc (new_size); - zstream.avail_out = new_size; - zstream.next_out = (Bytef *) new_data; - - do { - result = inflate (&zstream, Z_NO_FLUSH); - if (result != Z_OK && result != Z_STREAM_END) { - GST_WARNING ("zlib decompression failed."); - g_free (new_data); - inflateEnd (&zstream); - break; - } - new_size += 4000; - new_data = g_realloc (new_data, new_size); - zstream.next_out = (Bytef *) (new_data + zstream.total_out); - zstream.avail_out += 4000; - } while (zstream.avail_in != 0 && result != Z_STREAM_END); - - if (result != Z_STREAM_END) { - ret = FALSE; - goto out; - } else { - new_size = zstream.total_out; - inflateEnd (&zstream); - } -#else - GST_WARNING ("zlib encoded tracks not supported."); - ret = FALSE; - goto out; -#endif - } else if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_BZLIB) { -#ifdef HAVE_BZ2 - /* bzip2 encoded data */ - bz_stream bzstream; - guint orig_size; - int result; - - bzstream.bzalloc = NULL; - bzstream.bzfree = NULL; - bzstream.opaque = NULL; - orig_size = size; - - if (BZ2_bzDecompressInit (&bzstream, 0, 0) != BZ_OK) { - GST_WARNING ("bzip2 initialization failed."); - ret = FALSE; - goto out; - } - - bzstream.next_in = (char *) data; - bzstream.avail_in = orig_size; - new_size = orig_size; - new_data = g_malloc (new_size); - bzstream.avail_out = new_size; - bzstream.next_out = (char *) new_data; - - do { - result = BZ2_bzDecompress (&bzstream); - if (result != BZ_OK && result != BZ_STREAM_END) { - GST_WARNING ("bzip2 decompression failed."); - g_free (new_data); - BZ2_bzDecompressEnd (&bzstream); - break; - } - new_size += 4000; - new_data = g_realloc (new_data, new_size); - bzstream.next_out = (char *) (new_data + bzstream.total_out_lo32); - bzstream.avail_out += 4000; - } while (bzstream.avail_in != 0 && result != BZ_STREAM_END); - - if (result != BZ_STREAM_END) { - ret = FALSE; - goto out; - } else { - new_size = bzstream.total_out_lo32; - BZ2_bzDecompressEnd (&bzstream); - } -#else - GST_WARNING ("bzip2 encoded tracks not supported."); - ret = FALSE; - goto out; -#endif - } else if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_LZO1X) { - /* lzo encoded data */ - int result; - int orig_size, out_size; - - orig_size = size; - out_size = size; - new_size = size; - new_data = g_malloc (new_size); - - do { - orig_size = size; - out_size = new_size; - - result = lzo1x_decode (new_data, &out_size, data, &orig_size); - - if (orig_size > 0) { - new_size += 4000; - new_data = g_realloc (new_data, new_size); - } - } while (orig_size > 0 && result == LZO_OUTPUT_FULL); - - new_size -= out_size; - - if (result != LZO_OUTPUT_FULL) { - GST_WARNING ("lzo decompression failed"); - g_free (new_data); - - ret = FALSE; - goto out; - } - - } else if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_HEADERSTRIP) { - /* header stripped encoded data */ - if (enc->comp_settings_length > 0) { - new_data = g_malloc (size + enc->comp_settings_length); - new_size = size + enc->comp_settings_length; - - memcpy (new_data, enc->comp_settings, enc->comp_settings_length); - memcpy (new_data + enc->comp_settings_length, data, size); - } - } else { - GST_ERROR ("invalid compression algorithm %d", algo); - ret = FALSE; - } - -out: - - if (!ret) { - *data_out = NULL; - *size_out = 0; - } else { - *data_out = new_data; - *size_out = new_size; - } - - return ret; -} - static gboolean gst_matroska_decode_data (GArray * encodings, guint8 ** data_out, guint * size_out, GstMatroskaTrackEncodingScope scope, gboolean free) @@ -949,49 +750,6 @@ gst_matroska_decode_data (GArray * encodings, guint8 ** data_out, return ret; } -static GstFlowReturn -gst_matroska_decode_content_encodings (GArray * encodings) -{ - gint i; - - if (encodings == NULL) - return GST_FLOW_OK; - - for (i = 0; i < encodings->len; i++) { - GstMatroskaTrackEncoding *enc = - &g_array_index (encodings, GstMatroskaTrackEncoding, i); - 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) @@ -1041,9 +799,10 @@ gst_matroska_parse_tracknumber_unique (GstMatroskaParse * parse, guint64 num) { gint i; - g_assert (parse->src->len == parse->num_streams); - for (i = 0; i < parse->src->len; i++) { - GstMatroskaTrackContext *context = g_ptr_array_index (parse->src, i); + 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; @@ -1070,8 +829,8 @@ gst_matroska_parse_add_stream (GstMatroskaParse * parse, GstEbmlRead * ebml) /* 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; @@ -1083,8 +842,8 @@ gst_matroska_parse_add_stream (GstMatroskaParse * parse, GstEbmlRead * ebml) 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); @@ -1176,7 +935,8 @@ gst_matroska_parse_add_stream (GstMatroskaParse * parse, GstEbmlRead * ebml) 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; } @@ -1195,7 +955,8 @@ gst_matroska_parse_add_stream (GstMatroskaParse * parse, GstEbmlRead * ebml) 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)) { @@ -1415,7 +1176,8 @@ gst_matroska_parse_add_stream (GstMatroskaParse * parse, GstEbmlRead * ebml) 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)) { @@ -1715,9 +1477,9 @@ gst_matroska_parse_add_stream (GstMatroskaParse * parse, GstEbmlRead * ebml) 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); } @@ -1880,14 +1642,14 @@ gst_matroskaparse_do_index_seek (GstMatroskaParse * parse, 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, @@ -1970,10 +1732,10 @@ gst_matroska_parse_get_seek_track (GstMatroskaParse * parse, 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; } @@ -1989,9 +1751,10 @@ gst_matroska_parse_reset_streams (GstMatroskaParse * parse, GstClockTime time, 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; @@ -2153,7 +1916,8 @@ gst_matroska_parse_handle_seek_event (GstMatroskaParse * parse, /* need to seek to cluster start to pick up cluster time */ /* upstream takes care of flushing and all that * ... and newsegment event handling takes care of the rest */ - return perform_seek_to_offset (parse, entry->pos + parse->ebml_segment_start); + return perform_seek_to_offset (parse, entry->pos + + parse->common.ebml_segment_start); } /* @@ -2198,7 +1962,7 @@ gst_matroska_parse_handle_seek_push (GstMatroskaParse * parse, GstPad * pad, } /* check for having parsed index already */ - if (!parse->index_parsed) { + if (!parse->common.index_parsed) { gboolean building_index; guint64 offset = 0; @@ -2209,7 +1973,7 @@ gst_matroska_parse_handle_seek_push (GstMatroskaParse * parse, GstPad * pad, 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 */ @@ -2249,7 +2013,7 @@ gst_matroska_parse_handle_src_event (GstPad * pad, GstEvent * 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; } @@ -2295,24 +2059,6 @@ gst_matroska_parse_handle_src_event (GstPad * pad, GstEvent * event) 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) { @@ -2424,7 +2170,8 @@ 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; @@ -2493,7 +2240,8 @@ gst_matroska_parse_parse_tracks (GstMatroskaParse * parse, GstEbmlRead * ebml) 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; } } @@ -2504,306 +2252,6 @@ gst_matroska_parse_parse_tracks (GstMatroskaParse * parse, GstEbmlRead * ebml) return ret; } -static GstFlowReturn -gst_matroska_parse_parse_index_cuetrack (GstMatroskaParse * parse, - GstEbmlRead * ebml, guint * nentries) -{ - guint32 id; - GstFlowReturn ret; - GstMatroskaIndex idx; - - idx.pos = (guint64) - 1; - idx.track = 0; - idx.time = GST_CLOCK_TIME_NONE; - idx.block = 1; - - DEBUG_ELEMENT_START (parse, ebml, "CueTrackPositions"); - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (parse, ebml, "CueTrackPositions", ret); - return ret; - } - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - /* track number */ - case GST_MATROSKA_ID_CUETRACK: - { - guint64 num; - - if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) - break; - - if (num == 0) { - idx.track = 0; - GST_WARNING_OBJECT (parse, "Invalid CueTrack 0"); - break; - } - - GST_DEBUG_OBJECT (parse, "CueTrack: %" G_GUINT64_FORMAT, num); - idx.track = num; - break; - } - - /* position in file */ - case GST_MATROSKA_ID_CUECLUSTERPOSITION: - { - guint64 num; - - if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) - break; - - if (num > G_MAXINT64) { - GST_WARNING_OBJECT (parse, "CueClusterPosition %" G_GUINT64_FORMAT - " too large", num); - break; - } - - idx.pos = num; - break; - } - - /* number of block in the cluster */ - case GST_MATROSKA_ID_CUEBLOCKNUMBER: - { - guint64 num; - - if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) - break; - - if (num == 0) { - GST_WARNING_OBJECT (parse, "Invalid CueBlockNumber 0"); - break; - } - - GST_DEBUG_OBJECT (parse, "CueBlockNumber: %" G_GUINT64_FORMAT, num); - idx.block = num; - - /* mild sanity check, disregard strange cases ... */ - if (idx.block > G_MAXUINT16) { - GST_DEBUG_OBJECT (parse, "... looks suspicious, ignoring"); - idx.block = 1; - } - break; - } - - default: - ret = gst_matroska_parse_parse_skip (parse, ebml, "CueTrackPositions", - id); - break; - - case GST_MATROSKA_ID_CUECODECSTATE: - case GST_MATROSKA_ID_CUEREFERENCE: - ret = gst_ebml_read_skip (ebml); - break; - } - } - - DEBUG_ELEMENT_STOP (parse, ebml, "CueTrackPositions", ret); - - if ((ret == GST_FLOW_OK || ret == GST_FLOW_UNEXPECTED) - && idx.pos != (guint64) - 1 && idx.track > 0) { - g_array_append_val (parse->index, idx); - (*nentries)++; - } else if (ret == GST_FLOW_OK || ret == GST_FLOW_UNEXPECTED) { - GST_DEBUG_OBJECT (parse, "CueTrackPositions without valid content"); - } - - return ret; -} - -static GstFlowReturn -gst_matroska_parse_parse_index_pointentry (GstMatroskaParse * parse, - GstEbmlRead * ebml) -{ - guint32 id; - GstFlowReturn ret; - GstClockTime time = GST_CLOCK_TIME_NONE; - guint nentries = 0; - - DEBUG_ELEMENT_START (parse, ebml, "CuePoint"); - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (parse, ebml, "CuePoint", ret); - return ret; - } - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - /* one single index entry ('point') */ - case GST_MATROSKA_ID_CUETIME: - { - if ((ret = gst_ebml_read_uint (ebml, &id, &time)) != GST_FLOW_OK) - break; - - GST_DEBUG_OBJECT (parse, "CueTime: %" G_GUINT64_FORMAT, time); - time = time * parse->time_scale; - break; - } - - /* position in the file + track to which it belongs */ - case GST_MATROSKA_ID_CUETRACKPOSITIONS: - { - if ((ret = - gst_matroska_parse_parse_index_cuetrack (parse, ebml, - &nentries)) != GST_FLOW_OK) - break; - break; - } - - default: - ret = gst_matroska_parse_parse_skip (parse, ebml, "CuePoint", id); - break; - } - } - - DEBUG_ELEMENT_STOP (parse, ebml, "CuePoint", ret); - - if (nentries > 0) { - if (time == GST_CLOCK_TIME_NONE) { - GST_WARNING_OBJECT (parse, "CuePoint without valid time"); - g_array_remove_range (parse->index, parse->index->len - nentries, - nentries); - } else { - gint i; - - for (i = parse->index->len - nentries; i < parse->index->len; i++) { - GstMatroskaIndex *idx = - &g_array_index (parse->index, GstMatroskaIndex, i); - - idx->time = time; - GST_DEBUG_OBJECT (parse, "Index entry: pos=%" G_GUINT64_FORMAT - ", time=%" GST_TIME_FORMAT ", track=%u, block=%u", idx->pos, - GST_TIME_ARGS (idx->time), (guint) idx->track, (guint) idx->block); - } - } - } else { - GST_DEBUG_OBJECT (parse, "Empty CuePoint"); - } - - return ret; -} - -static gint -gst_matroska_index_compare (GstMatroskaIndex * i1, GstMatroskaIndex * i2) -{ - if (i1->time < i2->time) - return -1; - else if (i1->time > i2->time) - return 1; - else if (i1->block < i2->block) - return -1; - else if (i1->block > i2->block) - return 1; - else - return 0; -} - -static GstFlowReturn -gst_matroska_parse_parse_index (GstMatroskaParse * parse, GstEbmlRead * ebml) -{ - guint32 id; - GstFlowReturn ret = GST_FLOW_OK; - guint i; - - if (parse->index) - g_array_free (parse->index, TRUE); - parse->index = - g_array_sized_new (FALSE, FALSE, sizeof (GstMatroskaIndex), 128); - - DEBUG_ELEMENT_START (parse, ebml, "Cues"); - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (parse, ebml, "Cues", ret); - return ret; - } - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - /* one single index entry ('point') */ - case GST_MATROSKA_ID_POINTENTRY: - ret = gst_matroska_parse_parse_index_pointentry (parse, ebml); - break; - - default: - ret = gst_matroska_parse_parse_skip (parse, ebml, "Cues", id); - break; - } - } - DEBUG_ELEMENT_STOP (parse, ebml, "Cues", ret); - - /* Sort index by time, smallest time first, for easier searching */ - g_array_sort (parse->index, (GCompareFunc) gst_matroska_index_compare); - - /* Now sort the track specific index entries into their own arrays */ - for (i = 0; i < parse->index->len; i++) { - GstMatroskaIndex *idx = &g_array_index (parse->index, GstMatroskaIndex, i); - gint track_num; - GstMatroskaTrackContext *ctx; - - if (parse->element_index) { - gint writer_id; - - if (idx->track != 0 && - (track_num = - gst_matroska_parse_stream_from_num (parse, idx->track)) != -1) { - ctx = g_ptr_array_index (parse->src, track_num); - - if (ctx->index_writer_id == -1) - gst_index_get_writer_id (parse->element_index, GST_OBJECT (ctx->pad), - &ctx->index_writer_id); - writer_id = ctx->index_writer_id; - } else { - if (parse->element_index_writer_id == -1) - gst_index_get_writer_id (parse->element_index, GST_OBJECT (parse), - &parse->element_index_writer_id); - writer_id = parse->element_index_writer_id; - } - - GST_LOG_OBJECT (parse, "adding association %" GST_TIME_FORMAT "-> %" - G_GUINT64_FORMAT " for writer id %d", GST_TIME_ARGS (idx->time), - idx->pos, writer_id); - gst_index_add_association (parse->element_index, writer_id, - GST_ASSOCIATION_FLAG_KEY_UNIT, GST_FORMAT_TIME, idx->time, - GST_FORMAT_BYTES, idx->pos + parse->ebml_segment_start, NULL); - } - - if (idx->track == 0) - continue; - - track_num = gst_matroska_parse_stream_from_num (parse, idx->track); - if (track_num == -1) - continue; - - ctx = g_ptr_array_index (parse->src, track_num); - - if (ctx->index_table == NULL) - ctx->index_table = - g_array_sized_new (FALSE, FALSE, sizeof (GstMatroskaIndex), 128); - - g_array_append_vals (ctx->index_table, idx, 1); - } - - parse->index_parsed = TRUE; - - /* sanity check; empty index normalizes to no index */ - if (parse->index->len == 0) { - g_array_free (parse->index, TRUE); - parse->index = NULL; - } - - return ret; -} - static GstFlowReturn gst_matroska_parse_parse_info (GstMatroskaParse * parse, GstEbmlRead * ebml) { @@ -2832,7 +2280,7 @@ gst_matroska_parse_parse_info (GstMatroskaParse * parse, GstEbmlRead * ebml) GST_DEBUG_OBJECT (parse, "TimeCodeScale: %" G_GUINT64_FORMAT, num); - parse->time_scale = num; + parse->common.time_scale = num; break; } @@ -2899,7 +2347,8 @@ gst_matroska_parse_parse_info (GstMatroskaParse * parse, GstEbmlRead * ebml) } 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 */ @@ -2920,7 +2369,7 @@ gst_matroska_parse_parse_info (GstMatroskaParse * parse, GstEbmlRead * ebml) 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); } @@ -2994,7 +2443,8 @@ gst_matroska_parse_parse_metadata_id_simple_tag (GstMatroskaParse * parse, 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 */ @@ -3080,7 +2530,8 @@ gst_matroska_parse_parse_metadata_id_tag (GstMatroskaParse * parse, 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; } } @@ -3136,7 +2587,8 @@ gst_matroska_parse_parse_metadata (GstMatroskaParse * parse, GstEbmlRead * ebml) 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: @@ -3219,7 +2671,8 @@ gst_matroska_parse_parse_attached_file (GstMatroskaParse * parse, 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); @@ -3332,7 +2785,8 @@ gst_matroska_parse_parse_attachments (GstMatroskaParse * parse, 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; } } @@ -3498,14 +2952,15 @@ gst_matroska_parse_parse_blockgroup_or_simpleblock (GstMatroskaParse * parse, 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 @@ -3513,7 +2968,7 @@ gst_matroska_parse_parse_blockgroup_or_simpleblock (GstMatroskaParse * parse, 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)); @@ -3640,7 +3095,8 @@ gst_matroska_parse_parse_blockgroup_or_simpleblock (GstMatroskaParse * parse, } 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: @@ -3667,7 +3123,7 @@ gst_matroska_parse_parse_blockgroup_or_simpleblock (GstMatroskaParse * parse, 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? @@ -3676,11 +3132,11 @@ gst_matroska_parse_parse_blockgroup_or_simpleblock (GstMatroskaParse * parse, 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; @@ -3707,11 +3163,12 @@ gst_matroska_parse_parse_blockgroup_or_simpleblock (GstMatroskaParse * parse, 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; @@ -4041,7 +3498,8 @@ gst_matroska_parse_parse_contents_seekentry (GstMatroskaParse * parse, } default: - ret = gst_matroska_parse_parse_skip (parse, ebml, "SeekHead", id); + ret = gst_matroska_read_common_parse_skip (&parse->common, ebml, + "SeekHead", id); break; } } @@ -4077,17 +3535,18 @@ gst_matroska_parse_parse_contents_seekentry (GstMatroskaParse * parse, } /* 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); } @@ -4133,7 +3592,8 @@ gst_matroska_parse_parse_contents (GstMatroskaParse * parse, GstEbmlRead * ebml) } default: - ret = gst_matroska_parse_parse_skip (parse, ebml, "SeekHead", id); + ret = gst_matroska_read_common_parse_skip (&parse->common, ebml, + "SeekHead", id); break; } } @@ -4415,15 +3875,15 @@ gst_matroska_parse_parse_id (GstMatroskaParse * parse, guint32 id, 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; @@ -4432,7 +3892,7 @@ gst_matroska_parse_parse_id (GstMatroskaParse * parse, guint32 id, break; } break; - case GST_MATROSKA_PARSE_STATE_SEGMENT: + case GST_MATROSKA_READ_STATE_SEGMENT: switch (id) { case GST_MATROSKA_ID_SEGMENT: /* eat segment prefix */ @@ -4442,8 +3902,8 @@ gst_matroska_parse_parse_id (GstMatroskaParse * parse, guint32 id, 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: @@ -4455,14 +3915,14 @@ gst_matroska_parse_parse_id (GstMatroskaParse * parse, guint32 id, 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)); @@ -4483,8 +3943,9 @@ gst_matroska_parse_parse_id (GstMatroskaParse * parse, guint32 id, 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"); } @@ -4514,16 +3975,17 @@ gst_matroska_parse_parse_id (GstMatroskaParse * parse, guint32 id, 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); } @@ -4576,11 +4038,11 @@ gst_matroska_parse_parse_id (GstMatroskaParse * parse, guint32 id, 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); @@ -4594,7 +4056,7 @@ gst_matroska_parse_parse_id (GstMatroskaParse * parse, guint32 id, 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); } } @@ -4679,7 +4141,7 @@ gst_matroska_parse_loop (GstPad * pad) 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; @@ -4917,7 +4379,7 @@ gst_matroska_parse_handle_sink_event (GstPad * pad, GstEvent * event) "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; } @@ -4951,11 +4413,11 @@ gst_matroska_parse_handle_sink_event (GstPad * pad, GstEvent * event) } 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 { @@ -4988,11 +4450,12 @@ gst_matroska_parse_set_index (GstElement * element, GstIndex * index) GstMatroskaParse *parse = GST_MATROSKA_PARSE (element); GST_OBJECT_LOCK (parse); - if (parse->element_index) - gst_object_unref (parse->element_index); - parse->element_index = index ? gst_object_ref (index) : NULL; + 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 * @@ -5002,8 +4465,8 @@ gst_matroska_parse_get_index (GstElement * element) 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); diff --git a/gst/matroska/matroska-parse.h b/gst/matroska/matroska-parse.h index 595ead2e3c..98b399d3bc 100644 --- a/gst/matroska/matroska-parse.h +++ b/gst/matroska/matroska-parse.h @@ -1,5 +1,6 @@ /* GStreamer Matroska muxer/demuxer * (c) 2003 Ronald Bultje + * (c) 2011 Debarshi Ray * * matroska-parse.h: matroska file/stream parseer definition * @@ -27,6 +28,7 @@ #include "ebml-read.h" #include "matroska-ids.h" +#include "matroska-read-common.h" G_BEGIN_DECLS @@ -41,29 +43,17 @@ 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; @@ -79,28 +69,17 @@ typedef struct _GstMatroskaParse { /* 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; diff --git a/gst/matroska/matroska-read-common.c b/gst/matroska/matroska-read-common.c new file mode 100644 index 0000000000..5729688570 --- /dev/null +++ b/gst/matroska/matroska-read-common.c @@ -0,0 +1,609 @@ +/* GStreamer Matroska muxer/demuxer + * (c) 2003 Ronald Bultje + * (c) 2006 Tim-Philipp Müller + * (c) 2008 Sebastian Dröge + * (c) 2011 Debarshi Ray + * + * 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 + +#ifdef HAVE_ZLIB +#include +#endif + +#ifdef HAVE_BZ2 +#include +#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; +} diff --git a/gst/matroska/matroska-read-common.h b/gst/matroska/matroska-read-common.h new file mode 100644 index 0000000000..cbe7103232 --- /dev/null +++ b/gst/matroska/matroska-read-common.h @@ -0,0 +1,79 @@ +/* GStreamer Matroska muxer/demuxer + * (c) 2003 Ronald Bultje + * (c) 2011 Debarshi Ray + * + * 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 +#include + +#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__ */ diff --git a/gst/rtp/gstrtpac3depay.c b/gst/rtp/gstrtpac3depay.c index 099be64a9a..17da501fe7 100644 --- a/gst/rtp/gstrtpac3depay.c +++ b/gst/rtp/gstrtpac3depay.c @@ -190,8 +190,9 @@ gst_rtp_ac3_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) 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; diff --git a/gst/rtp/gstrtpbvdepay.c b/gst/rtp/gstrtpbvdepay.c index a01aa479b2..7b8d67f095 100644 --- a/gst/rtp/gstrtpbvdepay.c +++ b/gst/rtp/gstrtpbvdepay.c @@ -166,7 +166,7 @@ gst_rtp_bv_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) 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); } diff --git a/gst/rtp/gstrtpg722depay.c b/gst/rtp/gstrtpg722depay.c index 8f0f2c5fc9..e792e600b2 100644 --- a/gst/rtp/gstrtpg722depay.c +++ b/gst/rtp/gstrtpg722depay.c @@ -235,7 +235,7 @@ gst_rtp_g722_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) 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); } diff --git a/gst/rtp/gstrtpg726depay.c b/gst/rtp/gstrtpg726depay.c index cf88bd6e5f..2f34a0dfc9 100644 --- a/gst/rtp/gstrtpg726depay.c +++ b/gst/rtp/gstrtpg726depay.c @@ -222,6 +222,8 @@ gst_rtp_g726_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) 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; @@ -231,6 +233,8 @@ gst_rtp_g726_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) 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); @@ -331,6 +335,9 @@ gst_rtp_g726_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) } return outbuf; + +bad_len: + return NULL; } static void diff --git a/gst/rtp/gstrtpgsmdepay.c b/gst/rtp/gstrtpgsmdepay.c index 30f6a7f715..298708af6d 100644 --- a/gst/rtp/gstrtpgsmdepay.c +++ b/gst/rtp/gstrtpgsmdepay.c @@ -136,7 +136,7 @@ gst_rtp_gsm_depay_process (GstBaseRTPDepayload * _depayload, GstBuffer * buf) 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); } diff --git a/gst/rtp/gstrtpilbcdepay.c b/gst/rtp/gstrtpilbcdepay.c index 6c22596b3c..f8e8ab5144 100644 --- a/gst/rtp/gstrtpilbcdepay.c +++ b/gst/rtp/gstrtpilbcdepay.c @@ -190,7 +190,7 @@ gst_rtp_ilbc_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) 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); } diff --git a/gst/rtp/gstrtpjpegpay.c b/gst/rtp/gstrtpjpegpay.c index fc4096d5c2..f22384709f 100644 --- a/gst/rtp/gstrtpjpegpay.c +++ b/gst/rtp/gstrtpjpegpay.c @@ -716,7 +716,7 @@ gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * basepayload, guint qt; qt = info[i].qt; - if (qt > 15) + if (qt >= G_N_ELEMENTS (tables)) goto invalid_quant; qsize = tables[qt].size; diff --git a/gst/rtp/gstrtpmp1sdepay.c b/gst/rtp/gstrtpmp1sdepay.c index 4887c4cdea..b7063d6d3e 100644 --- a/gst/rtp/gstrtpmp1sdepay.c +++ b/gst/rtp/gstrtpmp1sdepay.c @@ -131,8 +131,9 @@ gst_rtp_mp1s_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) 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; } diff --git a/gst/rtp/gstrtpmp2tdepay.c b/gst/rtp/gstrtpmp2tdepay.c index fe6c5dee2a..4f5e7202e0 100644 --- a/gst/rtp/gstrtpmp2tdepay.c +++ b/gst/rtp/gstrtpmp2tdepay.c @@ -169,8 +169,9 @@ gst_rtp_mp2t_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) 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; diff --git a/gst/rtp/gstrtpmp4adepay.c b/gst/rtp/gstrtpmp4adepay.c index 18ebfd2190..664864abe1 100644 --- a/gst/rtp/gstrtpmp4adepay.c +++ b/gst/rtp/gstrtpmp4adepay.c @@ -51,7 +51,7 @@ GST_STATIC_PAD_TEMPLATE ("sink", /* All optional parameters * * "profile-level-id=[1,MAX]" - * "config=" + * "config=" */ ) ); @@ -229,7 +229,7 @@ gst_rtp_mp4a_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) 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; } diff --git a/gst/rtp/gstrtpmpvdepay.c b/gst/rtp/gstrtpmpvdepay.c index d6e856bec9..c94eb63af4 100644 --- a/gst/rtp/gstrtpmpvdepay.c +++ b/gst/rtp/gstrtpmpvdepay.c @@ -172,10 +172,11 @@ gst_rtp_mpv_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) 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; } diff --git a/gst/rtp/gstrtppcmadepay.c b/gst/rtp/gstrtppcmadepay.c index 27d463f7c4..4d4a80ede3 100644 --- a/gst/rtp/gstrtppcmadepay.c +++ b/gst/rtp/gstrtppcmadepay.c @@ -46,18 +46,18 @@ static GstStaticPadTemplate gst_rtp_pcma_depay_sink_template = 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, @@ -144,12 +144,14 @@ gst_rtp_pcma_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) 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); + } } diff --git a/gst/rtp/gstrtppcmudepay.c b/gst/rtp/gstrtppcmudepay.c index 0a208cdc96..36dfd46ef1 100644 --- a/gst/rtp/gstrtppcmudepay.c +++ b/gst/rtp/gstrtppcmudepay.c @@ -46,18 +46,19 @@ static GstStaticPadTemplate gst_rtp_pcmu_depay_sink_template = 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, @@ -144,12 +145,14 @@ gst_rtp_pcmu_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) 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; diff --git a/gst/rtp/gstrtpqcelpdepay.c b/gst/rtp/gstrtpqcelpdepay.c index 7949ae7804..ce947d4c01 100644 --- a/gst/rtp/gstrtpqcelpdepay.c +++ b/gst/rtp/gstrtpqcelpdepay.c @@ -164,7 +164,7 @@ static const gint frame_size[16] = { 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]; diff --git a/gst/rtp/gstrtpspeexdepay.c b/gst/rtp/gstrtpspeexdepay.c index 10dea1a703..87e8a77297 100644 --- a/gst/rtp/gstrtpspeexdepay.c +++ b/gst/rtp/gstrtpspeexdepay.c @@ -212,7 +212,8 @@ gst_rtp_speex_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) /* 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; } diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index 3a463605f3..d35581328e 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -5502,8 +5502,10 @@ restart: 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) diff --git a/sys/v4l2/gstv4l2object.c b/sys/v4l2/gstv4l2object.c index 7695956693..3a550340f8 100644 --- a/sys/v4l2/gstv4l2object.c +++ b/sys/v4l2/gstv4l2object.c @@ -56,7 +56,7 @@ GST_DEBUG_CATEGORY_EXTERN (v4l2_debug); #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 @@ -316,6 +316,58 @@ gst_v4l2_device_get_type (void) 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) @@ -384,6 +436,19 @@ gst_v4l2_object_install_properties_helper (GObjectClass * gobject_class, "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 * @@ -440,9 +505,6 @@ gst_v4l2_object_destroy (GstV4l2Object * v4l2object) if (v4l2object->channel) g_free (v4l2object->channel); - if (v4l2object->norm) - g_free (v4l2object->norm); - if (v4l2object->formats) { gst_v4l2_object_clear_format_list (v4l2object); } @@ -510,23 +572,10 @@ gst_v4l2_object_set_property_helper (GstV4l2Object * 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); @@ -631,6 +680,9 @@ gst_v4l2_object_get_property_helper (GstV4l2Object * v4l2object, return TRUE; } break; + case PROP_TV_NORM: + g_value_set_enum (value, v4l2object->tv_norm); + break; default: return FALSE; break; @@ -650,16 +702,18 @@ gst_v4l2_set_defaults (GstV4l2Object * v4l2object) 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); } } @@ -1887,13 +1941,15 @@ default_frame_sizes: } /* 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 */ diff --git a/sys/v4l2/gstv4l2object.h b/sys/v4l2/gstv4l2object.h index a0dd41ce11..a7b590d9b9 100644 --- a/sys/v4l2/gstv4l2object.h +++ b/sys/v4l2/gstv4l2object.h @@ -108,7 +108,7 @@ struct _GstV4l2Object { GList *channels; /* properties */ - gchar *norm; + v4l2_std_id tv_norm; gchar *channel; gulong frequency; @@ -133,11 +133,12 @@ GType gst_v4l2_object_get_type (void); 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, diff --git a/sys/v4l2/gstv4l2sink.c b/sys/v4l2/gstv4l2sink.c index fcf7e87327..1c8108b96e 100644 --- a/sys/v4l2/gstv4l2sink.c +++ b/sys/v4l2/gstv4l2sink.c @@ -55,6 +55,7 @@ #include "gstv4l2colorbalance.h" +#include "gstv4l2tuner.h" #ifdef HAVE_XVIDEO #include "gstv4l2xoverlay.h" #endif @@ -91,6 +92,7 @@ enum 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 @@ -105,10 +107,10 @@ gst_v4l2sink_iface_supported (GstImplementsInterface * iface, GType iface_type) 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) @@ -151,6 +153,11 @@ gst_v4l2sink_init_interfaces (GType type) 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, @@ -181,6 +188,7 @@ gst_v4l2sink_init_interfaces (GType type) g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, &v4l2iface_info); + g_type_add_interface_static (type, GST_TYPE_TUNER, &v4l2_tuner_info); #ifdef HAVE_XVIDEO g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &v4l2_xoverlay_info); g_type_add_interface_static (type, diff --git a/sys/v4l2/gstv4l2tuner.c b/sys/v4l2/gstv4l2tuner.c index c5f84426bd..a805396cac 100644 --- a/sys/v4l2/gstv4l2tuner.c +++ b/sys/v4l2/gstv4l2tuner.c @@ -237,7 +237,6 @@ gst_v4l2_tuner_set_norm (GstV4l2Object * v4l2object, GstTunerNorm * norm) 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 */ @@ -245,6 +244,14 @@ gst_v4l2_tuner_get_norm (GstV4l2Object * v4l2object) 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; @@ -253,6 +260,20 @@ gst_v4l2_tuner_get_norm (GstV4l2Object * v4l2object) 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) diff --git a/sys/v4l2/gstv4l2tuner.h b/sys/v4l2/gstv4l2tuner.h index bf9ff94da5..699ca87e10 100644 --- a/sys/v4l2/gstv4l2tuner.h +++ b/sys/v4l2/gstv4l2tuner.h @@ -94,6 +94,11 @@ void gst_v4l2_tuner_set_norm_and_notify (GstV4l2Object * v4l2o 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, diff --git a/sys/v4l2/v4l2_calls.c b/sys/v4l2/v4l2_calls.c index e9d306999f..3808d8e621 100644 --- a/sys/v4l2/v4l2_calls.c +++ b/sys/v4l2/v4l2_calls.c @@ -229,6 +229,9 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object) 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);