michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ 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 michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #if !defined(WebMReader_h_) michael@0: #define WebMReader_h_ michael@0: michael@0: #include michael@0: michael@0: #include "nsDeque.h" michael@0: #include "MediaDecoderReader.h" michael@0: #include "nsAutoRef.h" michael@0: #include "nestegg/nestegg.h" michael@0: michael@0: #define VPX_DONT_DEFINE_STDINT_TYPES michael@0: #include "vpx/vpx_codec.h" michael@0: michael@0: #ifdef MOZ_TREMOR michael@0: #include "tremor/ivorbiscodec.h" michael@0: #else michael@0: #include "vorbis/codec.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_OPUS michael@0: #include "OpusParser.h" michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: michael@0: class WebMBufferedState; michael@0: michael@0: // Holds a nestegg_packet, and its file offset. This is needed so we michael@0: // know the offset in the file we've played up to, in order to calculate michael@0: // whether it's likely we can play through to the end without needing michael@0: // to stop to buffer, given the current download rate. michael@0: class NesteggPacketHolder { michael@0: public: michael@0: NesteggPacketHolder(nestegg_packet* aPacket, int64_t aOffset) michael@0: : mPacket(aPacket), mOffset(aOffset) michael@0: { michael@0: MOZ_COUNT_CTOR(NesteggPacketHolder); michael@0: } michael@0: ~NesteggPacketHolder() { michael@0: MOZ_COUNT_DTOR(NesteggPacketHolder); michael@0: nestegg_free_packet(mPacket); michael@0: } michael@0: nestegg_packet* mPacket; michael@0: // Offset in bytes. This is the offset of the end of the Block michael@0: // which contains the packet. michael@0: int64_t mOffset; michael@0: private: michael@0: // Copy constructor and assignment operator not implemented. Don't use them! michael@0: NesteggPacketHolder(const NesteggPacketHolder &aOther); michael@0: NesteggPacketHolder& operator= (NesteggPacketHolder const& aOther); michael@0: }; michael@0: michael@0: // Thread and type safe wrapper around nsDeque. michael@0: class PacketQueueDeallocator : public nsDequeFunctor { michael@0: virtual void* operator() (void* anObject) { michael@0: delete static_cast(anObject); michael@0: return nullptr; michael@0: } michael@0: }; michael@0: michael@0: // Typesafe queue for holding nestegg packets. It has michael@0: // ownership of the items in the queue and will free them michael@0: // when destroyed. michael@0: class WebMPacketQueue : private nsDeque { michael@0: public: michael@0: WebMPacketQueue() michael@0: : nsDeque(new PacketQueueDeallocator()) michael@0: {} michael@0: michael@0: ~WebMPacketQueue() { michael@0: Reset(); michael@0: } michael@0: michael@0: inline int32_t GetSize() { michael@0: return nsDeque::GetSize(); michael@0: } michael@0: michael@0: inline void Push(NesteggPacketHolder* aItem) { michael@0: NS_ASSERTION(aItem, "NULL pushed to WebMPacketQueue"); michael@0: nsDeque::Push(aItem); michael@0: } michael@0: michael@0: inline void PushFront(NesteggPacketHolder* aItem) { michael@0: NS_ASSERTION(aItem, "NULL pushed to WebMPacketQueue"); michael@0: nsDeque::PushFront(aItem); michael@0: } michael@0: michael@0: inline NesteggPacketHolder* PopFront() { michael@0: return static_cast(nsDeque::PopFront()); michael@0: } michael@0: michael@0: void Reset() { michael@0: while (GetSize() > 0) { michael@0: delete PopFront(); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: class WebMReader : public MediaDecoderReader michael@0: { michael@0: public: michael@0: WebMReader(AbstractMediaDecoder* aDecoder); michael@0: ~WebMReader(); michael@0: michael@0: virtual nsresult Init(MediaDecoderReader* aCloneDonor); michael@0: virtual nsresult ResetDecode(); michael@0: virtual bool DecodeAudioData(); michael@0: michael@0: // If the Theora granulepos has not been captured, it may read several packets michael@0: // until one with a granulepos has been captured, to ensure that all packets michael@0: // read have valid time info. michael@0: virtual bool DecodeVideoFrame(bool &aKeyframeSkip, michael@0: int64_t aTimeThreshold); michael@0: michael@0: virtual bool HasAudio() michael@0: { michael@0: NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); michael@0: return mHasAudio; michael@0: } michael@0: michael@0: virtual bool HasVideo() michael@0: { michael@0: NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); michael@0: return mHasVideo; michael@0: } michael@0: michael@0: virtual nsresult ReadMetadata(MediaInfo* aInfo, michael@0: MetadataTags** aTags); michael@0: virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime); michael@0: virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime); michael@0: virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset); michael@0: michael@0: protected: michael@0: // Value passed to NextPacket to determine if we are reading a video or an michael@0: // audio packet. michael@0: enum TrackType { michael@0: VIDEO = 0, michael@0: AUDIO = 1 michael@0: }; michael@0: michael@0: // Read a packet from the nestegg file. Returns nullptr if all packets for michael@0: // the particular track have been read. Pass VIDEO or AUDIO to indicate the michael@0: // type of the packet we want to read. michael@0: nsReturnRef NextPacket(TrackType aTrackType); michael@0: michael@0: // Pushes a packet to the front of the video packet queue. michael@0: virtual void PushVideoPacket(NesteggPacketHolder* aItem); michael@0: michael@0: // Returns an initialized ogg packet with data obtained from the WebM container. michael@0: ogg_packet InitOggPacket(unsigned char* aData, michael@0: size_t aLength, michael@0: bool aBOS, michael@0: bool aEOS, michael@0: int64_t aGranulepos); michael@0: michael@0: #ifdef MOZ_OPUS michael@0: // Setup opus decoder michael@0: bool InitOpusDecoder(); michael@0: #endif michael@0: michael@0: // Decode a nestegg packet of audio data. Push the audio data on the michael@0: // audio queue. Returns true when there's more audio to decode, michael@0: // false if the audio is finished, end of file has been reached, michael@0: // or an un-recoverable read error has occured. The reader's monitor michael@0: // must be held during this call. The caller is responsible for freeing michael@0: // aPacket. michael@0: bool DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset); michael@0: michael@0: // Release context and set to null. Called when an error occurs during michael@0: // reading metadata or destruction of the reader itself. michael@0: void Cleanup(); michael@0: michael@0: private: michael@0: // libnestegg context for webm container. Access on state machine thread michael@0: // or decoder thread only. michael@0: nestegg* mContext; michael@0: michael@0: // VP8 decoder state michael@0: vpx_codec_ctx_t mVPX; michael@0: michael@0: // Vorbis decoder state michael@0: vorbis_info mVorbisInfo; michael@0: vorbis_comment mVorbisComment; michael@0: vorbis_dsp_state mVorbisDsp; michael@0: vorbis_block mVorbisBlock; michael@0: uint32_t mPacketCount; michael@0: uint32_t mChannels; michael@0: michael@0: michael@0: #ifdef MOZ_OPUS michael@0: // Opus decoder state michael@0: nsAutoPtr mOpusParser; michael@0: OpusMSDecoder *mOpusDecoder; michael@0: int mSkip; // Number of samples left to trim before playback. michael@0: uint64_t mSeekPreroll; // Number of nanoseconds that must be discarded after seeking. michael@0: #endif michael@0: michael@0: // Queue of video and audio packets that have been read but not decoded. These michael@0: // must only be accessed from the state machine thread. michael@0: WebMPacketQueue mVideoPackets; michael@0: WebMPacketQueue mAudioPackets; michael@0: michael@0: // Index of video and audio track to play michael@0: uint32_t mVideoTrack; michael@0: uint32_t mAudioTrack; michael@0: michael@0: // Time in microseconds of the start of the first audio frame we've decoded. michael@0: int64_t mAudioStartUsec; michael@0: michael@0: // Number of audio frames we've decoded since decoding began at mAudioStartMs. michael@0: uint64_t mAudioFrames; michael@0: michael@0: // Number of microseconds that must be discarded from the start of the Stream. michael@0: uint64_t mCodecDelay; michael@0: michael@0: // Parser state and computed offset-time mappings. Shared by multiple michael@0: // readers when decoder has been cloned. Main thread only. michael@0: nsRefPtr mBufferedState; michael@0: michael@0: // Size of the frame initially present in the stream. The picture region michael@0: // is defined as a ratio relative to this. michael@0: nsIntSize mInitialFrame; michael@0: michael@0: // Picture region, as relative to the initial frame size. michael@0: nsIntRect mPicture; michael@0: michael@0: // Codec ID of audio track michael@0: int mAudioCodec; michael@0: // Codec ID of video track michael@0: int mVideoCodec; michael@0: michael@0: // Booleans to indicate if we have audio and/or video data michael@0: bool mHasVideo; michael@0: bool mHasAudio; michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: #endif