michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #if !defined(GStreamerReader_h_) michael@0: #define GStreamerReader_h_ michael@0: michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: // This include trips -Wreserved-user-defined-literal on clang. Ignoring it michael@0: // trips -Wpragmas on GCC (unknown warning), but ignoring that trips michael@0: // -Wunknown-pragmas on clang (unknown pragma). michael@0: #pragma GCC diagnostic push michael@0: #pragma GCC diagnostic ignored "-Wunknown-pragmas" michael@0: #pragma GCC diagnostic ignored "-Wpragmas" michael@0: #pragma GCC diagnostic ignored "-Wreserved-user-defined-literal" michael@0: #include michael@0: #pragma GCC diagnostic pop michael@0: michael@0: #include "MediaDecoderReader.h" michael@0: #include "MP3FrameParser.h" michael@0: #include "ImageContainer.h" michael@0: #include "nsRect.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: namespace dom { michael@0: class TimeRanges; michael@0: } michael@0: michael@0: class AbstractMediaDecoder; michael@0: michael@0: class GStreamerReader : public MediaDecoderReader michael@0: { michael@0: typedef gfx::IntRect IntRect; michael@0: michael@0: public: michael@0: GStreamerReader(AbstractMediaDecoder* aDecoder); michael@0: virtual ~GStreamerReader(); michael@0: michael@0: virtual nsresult Init(MediaDecoderReader* aCloneDonor); michael@0: virtual nsresult ResetDecode(); michael@0: virtual bool DecodeAudioData(); michael@0: virtual bool DecodeVideoFrame(bool &aKeyframeSkip, michael@0: int64_t aTimeThreshold); michael@0: virtual nsresult ReadMetadata(MediaInfo* aInfo, michael@0: MetadataTags** aTags); michael@0: virtual nsresult Seek(int64_t aTime, michael@0: int64_t aStartTime, michael@0: int64_t aEndTime, michael@0: int64_t aCurrentTime); michael@0: virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime); michael@0: michael@0: virtual void NotifyDataArrived(const char *aBuffer, michael@0: uint32_t aLength, michael@0: int64_t aOffset) MOZ_OVERRIDE; michael@0: michael@0: virtual bool HasAudio() { michael@0: return mInfo.HasAudio(); michael@0: } michael@0: michael@0: virtual bool HasVideo() { michael@0: return mInfo.HasVideo(); michael@0: } michael@0: michael@0: layers::ImageContainer* GetImageContainer() { return mDecoder->GetImageContainer(); } michael@0: michael@0: private: michael@0: michael@0: void ReadAndPushData(guint aLength); michael@0: nsRefPtr GetImageFromBuffer(GstBuffer* aBuffer); michael@0: void CopyIntoImageBuffer(GstBuffer *aBuffer, GstBuffer** aOutBuffer, nsRefPtr &image); michael@0: GstCaps* BuildAudioSinkCaps(); michael@0: void InstallPadCallbacks(); michael@0: michael@0: #if GST_VERSION_MAJOR >= 1 michael@0: void ImageDataFromVideoFrame(GstVideoFrame *aFrame, layers::PlanarYCbCrImage::Data *aData); michael@0: #endif michael@0: michael@0: /* Called once the pipeline is setup to check that the stream only contains michael@0: * supported formats michael@0: */ michael@0: nsresult CheckSupportedFormats(); michael@0: michael@0: /* Gst callbacks */ michael@0: michael@0: static GstBusSyncReply ErrorCb(GstBus *aBus, GstMessage *aMessage, gpointer aUserData); michael@0: GstBusSyncReply Error(GstBus *aBus, GstMessage *aMessage); michael@0: michael@0: /* Called on the source-setup signal emitted by playbin. Used to michael@0: * configure appsrc . michael@0: */ michael@0: static void PlayBinSourceSetupCb(GstElement* aPlayBin, michael@0: GParamSpec* pspec, michael@0: gpointer aUserData); michael@0: void PlayBinSourceSetup(GstAppSrc* aSource); michael@0: michael@0: /* Called from appsrc when we need to read more data from the resource */ michael@0: static void NeedDataCb(GstAppSrc* aSrc, guint aLength, gpointer aUserData); michael@0: void NeedData(GstAppSrc* aSrc, guint aLength); michael@0: michael@0: /* Called when appsrc has enough data and we can stop reading */ michael@0: static void EnoughDataCb(GstAppSrc* aSrc, gpointer aUserData); michael@0: void EnoughData(GstAppSrc* aSrc); michael@0: michael@0: /* Called when a seek is issued on the pipeline */ michael@0: static gboolean SeekDataCb(GstAppSrc* aSrc, michael@0: guint64 aOffset, michael@0: gpointer aUserData); michael@0: gboolean SeekData(GstAppSrc* aSrc, guint64 aOffset); michael@0: michael@0: /* Called when events reach the sinks. See inline comments */ michael@0: #if GST_VERSION_MAJOR == 1 michael@0: static GstPadProbeReturn EventProbeCb(GstPad *aPad, GstPadProbeInfo *aInfo, gpointer aUserData); michael@0: GstPadProbeReturn EventProbe(GstPad *aPad, GstEvent *aEvent); michael@0: #else michael@0: static gboolean EventProbeCb(GstPad* aPad, GstEvent* aEvent, gpointer aUserData); michael@0: gboolean EventProbe(GstPad* aPad, GstEvent* aEvent); michael@0: #endif michael@0: michael@0: /* Called when the video part of the pipeline allocates buffers. Used to michael@0: * provide PlanarYCbCrImage backed GstBuffers to the pipeline so that a memory michael@0: * copy can be avoided when handling YUV buffers from the pipeline to the gfx michael@0: * side. michael@0: */ michael@0: #if GST_VERSION_MAJOR == 1 michael@0: static GstPadProbeReturn QueryProbeCb(GstPad *aPad, GstPadProbeInfo *aInfo, gpointer aUserData); michael@0: GstPadProbeReturn QueryProbe(GstPad *aPad, GstPadProbeInfo *aInfo, gpointer aUserData); michael@0: #else michael@0: static GstFlowReturn AllocateVideoBufferCb(GstPad* aPad, guint64 aOffset, guint aSize, michael@0: GstCaps* aCaps, GstBuffer** aBuf); michael@0: GstFlowReturn AllocateVideoBufferFull(GstPad* aPad, guint64 aOffset, guint aSize, michael@0: GstCaps* aCaps, GstBuffer** aBuf, nsRefPtr& aImage); michael@0: GstFlowReturn AllocateVideoBuffer(GstPad* aPad, guint64 aOffset, guint aSize, michael@0: GstCaps* aCaps, GstBuffer** aBuf); michael@0: #endif michael@0: michael@0: michael@0: /* Called when the pipeline is prerolled, that is when at start or after a michael@0: * seek, the first audio and video buffers are queued in the sinks. michael@0: */ michael@0: static GstFlowReturn NewPrerollCb(GstAppSink* aSink, gpointer aUserData); michael@0: void VideoPreroll(); michael@0: void AudioPreroll(); michael@0: michael@0: /* Called when buffers reach the sinks */ michael@0: static GstFlowReturn NewBufferCb(GstAppSink* aSink, gpointer aUserData); michael@0: void NewVideoBuffer(); michael@0: void NewAudioBuffer(); michael@0: michael@0: /* Called at end of stream, when decoding has finished */ michael@0: static void EosCb(GstAppSink* aSink, gpointer aUserData); michael@0: /* Notifies that a sink will no longer receive any more data. If nullptr michael@0: * is passed to this, we'll assume all streams have reached EOS (for example michael@0: * an error has occurred). */ michael@0: void Eos(GstAppSink* aSink = nullptr); michael@0: michael@0: /* Called when an element is added inside playbin. We use it to find the michael@0: * decodebin instance. michael@0: */ michael@0: static void PlayElementAddedCb(GstBin *aBin, GstElement *aElement, michael@0: gpointer *aUserData); michael@0: michael@0: /* Called during decoding, to decide whether a (sub)stream should be decoded or michael@0: * ignored */ michael@0: static bool ShouldAutoplugFactory(GstElementFactory* aFactory, GstCaps* aCaps); michael@0: michael@0: /* Called by decodebin during autoplugging. We use it to apply our michael@0: * container/codec whitelist. michael@0: */ michael@0: static GValueArray* AutoplugSortCb(GstElement* aElement, michael@0: GstPad* aPad, GstCaps* aCaps, michael@0: GValueArray* aFactories); michael@0: michael@0: // Try to find MP3 headers in this stream using our MP3 frame parser. michael@0: nsresult ParseMP3Headers(); michael@0: michael@0: // Get the length of the stream, excluding any metadata we have ignored at the michael@0: // start of the stream: ID3 headers, for example. michael@0: int64_t GetDataLength(); michael@0: michael@0: // Use our own MP3 parser here, largely for consistency with other platforms. michael@0: MP3FrameParser mMP3FrameParser; michael@0: michael@0: // The byte position in the stream where the actual media (ignoring, for michael@0: // example, ID3 tags) starts. michael@0: uint64_t mDataOffset; michael@0: michael@0: // We want to be able to decide in |ReadMetadata| whether or not we use the michael@0: // duration from the MP3 frame parser, as this backend supports more than just michael@0: // MP3. But |NotifyDataArrived| can update the duration and is often called michael@0: // _before_ |ReadMetadata|. This flag stops the former from using the parser michael@0: // duration until we are sure we want to. michael@0: bool mUseParserDuration; michael@0: int64_t mLastParserDuration; michael@0: michael@0: #if GST_VERSION_MAJOR >= 1 michael@0: GstAllocator *mAllocator; michael@0: GstBufferPool *mBufferPool; michael@0: GstVideoInfo mVideoInfo; michael@0: #endif michael@0: GstElement* mPlayBin; michael@0: GstBus* mBus; michael@0: GstAppSrc* mSource; michael@0: /* video sink bin */ michael@0: GstElement* mVideoSink; michael@0: /* the actual video app sink */ michael@0: GstAppSink* mVideoAppSink; michael@0: /* audio sink bin */ michael@0: GstElement* mAudioSink; michael@0: /* the actual audio app sink */ michael@0: GstAppSink* mAudioAppSink; michael@0: GstVideoFormat mFormat; michael@0: IntRect mPicture; michael@0: int mVideoSinkBufferCount; michael@0: int mAudioSinkBufferCount; michael@0: GstAppSrcCallbacks mSrcCallbacks; michael@0: GstAppSinkCallbacks mSinkCallbacks; michael@0: /* monitor used to synchronize access to shared state between gstreamer michael@0: * threads and other gecko threads */ michael@0: ReentrantMonitor mGstThreadsMonitor; michael@0: /* video and audio segments we use to convert absolute timestamps to [0, michael@0: * stream_duration]. They're set when the pipeline is started or after a seek. michael@0: * Concurrent access guarded with mGstThreadsMonitor. michael@0: */ michael@0: GstSegment mVideoSegment; michael@0: GstSegment mAudioSegment; michael@0: /* bool used to signal when gst has detected the end of stream and michael@0: * DecodeAudioData and DecodeVideoFrame should not expect any more data michael@0: */ michael@0: bool mReachedAudioEos; michael@0: bool mReachedVideoEos; michael@0: #if GST_VERSION_MAJOR >= 1 michael@0: bool mConfigureAlignment; michael@0: #endif michael@0: int fpsNum; michael@0: int fpsDen; michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: #endif