/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef WebMDemuxer_h_ #define WebMDemuxer_h_ #include "nsTArray.h" #include "MediaDataDemuxer.h" #include "MediaResource.h" #include "NesteggPacketHolder.h" #include #include #include typedef struct nestegg nestegg; namespace mozilla { class WebMBufferedState; // Queue for holding MediaRawData samples class MediaRawDataQueue { typedef std::deque> ContainerType; public: uint32_t GetSize() { return mQueue.size(); } void Push(MediaRawData* aItem) { mQueue.push_back(aItem); } void Push(already_AddRefed&& aItem) { mQueue.push_back(std::move(aItem)); } void PushFront(MediaRawData* aItem) { mQueue.push_front(aItem); } void PushFront(already_AddRefed&& aItem) { mQueue.push_front(std::move(aItem)); } void PushFront(MediaRawDataQueue&& aOther) { while (!aOther.mQueue.empty()) { PushFront(aOther.Pop()); } } already_AddRefed PopFront() { RefPtr result = std::move(mQueue.front()); mQueue.pop_front(); return result.forget(); } already_AddRefed Pop() { RefPtr result = std::move(mQueue.back()); mQueue.pop_back(); return result.forget(); } void Reset() { while (!mQueue.empty()) { mQueue.pop_front(); } } MediaRawDataQueue& operator=(const MediaRawDataQueue& aOther) = delete; const RefPtr& First() const { return mQueue.front(); } const RefPtr& Last() const { return mQueue.back(); } // Methods for range-based for loops. ContainerType::iterator begin() { return mQueue.begin(); } ContainerType::const_iterator begin() const { return mQueue.begin(); } ContainerType::iterator end() { return mQueue.end(); } ContainerType::const_iterator end() const { return mQueue.end(); } private: ContainerType mQueue; }; class WebMTrackDemuxer; DDLoggedTypeDeclNameAndBase(WebMDemuxer, MediaDataDemuxer); DDLoggedTypeNameAndBase(WebMTrackDemuxer, MediaTrackDemuxer); class WebMDemuxer : public MediaDataDemuxer, public DecoderDoctorLifeLogger { public: explicit WebMDemuxer(MediaResource* aResource); // Indicate if the WebMDemuxer is to be used with MediaSource. In which // case the demuxer will stop reads to the last known complete block. WebMDemuxer(MediaResource* aResource, bool aIsMediaSource); RefPtr Init() override; uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override; UniquePtr GetTrackInfo(TrackInfo::TrackType aType, size_t aTrackNumber) const; already_AddRefed GetTrackDemuxer( TrackInfo::TrackType aType, uint32_t aTrackNumber) override; bool IsSeekable() const override; bool IsSeekableOnlyInBufferedRanges() const override; UniquePtr GetCrypto() override; bool GetOffsetForTime(uint64_t aTime, int64_t* aOffset); // Demux next WebM packet and append samples to MediaRawDataQueue nsresult GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue* aSamples); void Reset(TrackInfo::TrackType aType); // Pushes a packet to the front of the audio packet queue. void PushAudioPacket(NesteggPacketHolder* aItem); // Pushes a packet to the front of the video packet queue. void PushVideoPacket(NesteggPacketHolder* aItem); // Public accessor for nestegg callbacks bool IsMediaSource() const { return mIsMediaSource; } int64_t LastWebMBlockOffset() const { return mLastWebMBlockOffset; } struct NestEggContext { NestEggContext(WebMDemuxer* aParent, MediaResource* aResource) : mParent(aParent), mResource(aResource), mContext(nullptr) {} ~NestEggContext(); int Init(); // Public accessor for nestegg callbacks bool IsMediaSource() const { return mParent->IsMediaSource(); } MediaResourceIndex* GetResource() { return &mResource; } int64_t GetEndDataOffset() const { return (!mParent->IsMediaSource() || mParent->LastWebMBlockOffset() < 0) ? mResource.GetLength() : mParent->LastWebMBlockOffset(); } WebMDemuxer* mParent; MediaResourceIndex mResource; nestegg* mContext; }; private: friend class WebMTrackDemuxer; ~WebMDemuxer(); void InitBufferedState(); nsresult ReadMetadata(); void NotifyDataArrived() override; void NotifyDataRemoved() override; void EnsureUpToDateIndex(); // A helper to catch bad intervals during `GetBuffered`. // Verifies if the interval given by start and end is valid, returning true if // it is, or false if not. Logs failure reason if the interval is invalid. bool IsBufferedIntervalValid(uint64_t start, uint64_t end); media::TimeIntervals GetBuffered(); nsresult SeekInternal(TrackInfo::TrackType aType, const media::TimeUnit& aTarget); CryptoTrack GetTrackCrypto(TrackInfo::TrackType aType, size_t aTrackNumber); // Read a packet from the nestegg file. Returns nullptr if all packets for // the particular track have been read. Pass TrackInfo::kVideoTrack or // TrackInfo::kVideoTrack to indicate the type of the packet we want to read. nsresult NextPacket(TrackInfo::TrackType aType, RefPtr& aPacket); // Internal method that demuxes the next packet from the stream. The caller // is responsible for making sure it doesn't get lost. nsresult DemuxPacket(TrackInfo::TrackType aType, RefPtr& aPacket); // libnestegg audio and video context for webm container. // Access on reader's thread only. NestEggContext mVideoContext; NestEggContext mAudioContext; MediaResourceIndex& Resource(TrackInfo::TrackType aType) { return aType == TrackInfo::kVideoTrack ? mVideoContext.mResource : mAudioContext.mResource; } nestegg* Context(TrackInfo::TrackType aType) const { return aType == TrackInfo::kVideoTrack ? mVideoContext.mContext : mAudioContext.mContext; } MediaInfo mInfo; nsTArray> mDemuxers; // Parser state and computed offset-time mappings. Shared by multiple // readers when decoder has been cloned. Main thread only. RefPtr mBufferedState; RefPtr mInitData; // Queue of video and audio packets that have been read but not decoded. WebMPacketQueue mVideoPackets; WebMPacketQueue mAudioPackets; // Index of video and audio track to play uint32_t mVideoTrack; uint32_t mAudioTrack; // Nanoseconds to discard after seeking. uint64_t mSeekPreroll; // Calculate the frame duration from the last decodeable frame using the // previous frame's timestamp. In NS. Maybe mLastAudioFrameTime; Maybe mLastVideoFrameTime; // Codec ID of audio track int mAudioCodec; // Codec ID of video track int mVideoCodec; // Booleans to indicate if we have audio and/or video data bool mHasVideo; bool mHasAudio; bool mNeedReIndex; // The last complete block parsed by the WebMBufferedState. -1 if not set. // We cache those values rather than retrieving them for performance reasons // as nestegg only performs 1-byte read at a time. int64_t mLastWebMBlockOffset; const bool mIsMediaSource; EncryptionInfo mCrypto; }; class WebMTrackDemuxer : public MediaTrackDemuxer, public DecoderDoctorLifeLogger { public: WebMTrackDemuxer(WebMDemuxer* aParent, TrackInfo::TrackType aType, uint32_t aTrackNumber); UniquePtr GetInfo() const override; RefPtr Seek(const media::TimeUnit& aTime) override; RefPtr GetSamples(int32_t aNumSamples = 1) override; void Reset() override; nsresult GetNextRandomAccessPoint(media::TimeUnit* aTime) override; RefPtr SkipToNextRandomAccessPoint( const media::TimeUnit& aTimeThreshold) override; media::TimeIntervals GetBuffered() override; int64_t GetEvictionOffset(const media::TimeUnit& aTime) override; void BreakCycles() override; private: friend class WebMDemuxer; ~WebMTrackDemuxer(); void UpdateSamples(const nsTArray>& aSamples); void SetNextKeyFrameTime(); nsresult NextSample(RefPtr& aData); RefPtr mParent; TrackInfo::TrackType mType; UniquePtr mInfo; Maybe mNextKeyframeTime; bool mNeedKeyframe; // Queued samples extracted by the demuxer, but not yet returned. MediaRawDataQueue mSamples; }; } // namespace mozilla #endif