michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ 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: #ifndef OMXCodecWrapper_h_ michael@0: #define OMXCodecWrapper_h_ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "AudioSegment.h" michael@0: #include "GonkNativeWindow.h" michael@0: #include "GonkNativeWindowClient.h" michael@0: michael@0: #include michael@0: michael@0: namespace android { michael@0: michael@0: class OMXAudioEncoder; michael@0: class OMXVideoEncoder; michael@0: michael@0: /** michael@0: * This class (and its subclasses) wraps the video and audio codec from michael@0: * MediaCodec API in libstagefright. Currently only AVC/H.264 video encoder and michael@0: * AAC audio encoder are supported. michael@0: * michael@0: * OMXCodecWrapper has static creator functions that returns actual codec michael@0: * instances for different types of codec supported and serves as superclass to michael@0: * provide a function to read encoded data as byte array from codec. Two michael@0: * subclasses, OMXAudioEncoder and OMXVideoEncoder, respectively provides michael@0: * functions for encoding data from audio and video track. michael@0: * michael@0: * A typical usage is as follows: michael@0: * - Call one of the creator function Create...() to get either a michael@0: * OMXAudioEncoder or OMXVideoEncoder object. michael@0: * - Configure codec by providing characteristics of input raw data, such as michael@0: * video frame width and height, using Configure(). michael@0: * - Send raw data (and notify end of stream) with Encode(). michael@0: * - Get encoded data through GetNextEncodedFrame(). michael@0: * - Repeat previous 2 steps until end of stream. michael@0: * - Destroy the object. michael@0: * michael@0: * The lifecycle of underlying OMX codec is binded with construction and michael@0: * destruction of OMXCodecWrapper and subclass objects. For some types of michael@0: * codecs, such as HW accelerated AVC/H.264 encoder, there can be only one michael@0: * instance system-wise at a time, attempting to create another instance will michael@0: * fail. michael@0: */ michael@0: class OMXCodecWrapper michael@0: { michael@0: public: michael@0: // Codec types. michael@0: enum CodecType { michael@0: AAC_ENC, // AAC encoder. michael@0: AMR_NB_ENC, // AMR_NB encoder. michael@0: AVC_ENC, // AVC/H.264 encoder. michael@0: TYPE_COUNT michael@0: }; michael@0: michael@0: // Input and output flags. michael@0: enum { michael@0: // For Encode() and Encode, it indicates the end of input stream; michael@0: // For GetNextEncodedFrame(), it indicates the end of output michael@0: // stream. michael@0: BUFFER_EOS = MediaCodec::BUFFER_FLAG_EOS, michael@0: // For GetNextEncodedFrame(). It indicates the output buffer is an I-frame. michael@0: BUFFER_SYNC_FRAME = MediaCodec::BUFFER_FLAG_SYNCFRAME, michael@0: // For GetNextEncodedFrame(). It indicates that the output buffer contains michael@0: // codec specific configuration info. (SPS & PPS for AVC/H.264; michael@0: // DecoderSpecificInfo for AAC) michael@0: BUFFER_CODEC_CONFIG = MediaCodec::BUFFER_FLAG_CODECCONFIG, michael@0: }; michael@0: michael@0: // Hard-coded values for AAC DecoderConfigDescriptor in libstagefright. michael@0: // See MPEG4Writer::Track::writeMp4aEsdsBox() michael@0: // Exposed for the need of MP4 container writer. michael@0: enum { michael@0: kAACBitrate = 96000, // kbps michael@0: kAACFrameSize = 768, // bytes michael@0: kAACFrameDuration = 1024, // How many samples per AAC frame. michael@0: }; michael@0: michael@0: /** Create a AAC audio encoder. Returns nullptr when failed. */ michael@0: static OMXAudioEncoder* CreateAACEncoder(); michael@0: michael@0: /** Create a AMR audio encoder. Returns nullptr when failed. */ michael@0: static OMXAudioEncoder* CreateAMRNBEncoder(); michael@0: michael@0: /** Create a AVC/H.264 video encoder. Returns nullptr when failed. */ michael@0: static OMXVideoEncoder* CreateAVCEncoder(); michael@0: michael@0: virtual ~OMXCodecWrapper(); michael@0: michael@0: /** michael@0: * Get the next available encoded data from MediaCodec. The data will be michael@0: * copied into aOutputBuf array, with its timestamp (in microseconds) in michael@0: * aOutputTimestamp. michael@0: * Wait at most aTimeout microseconds to dequeue a output buffer. michael@0: */ michael@0: nsresult GetNextEncodedFrame(nsTArray* aOutputBuf, michael@0: int64_t* aOutputTimestamp, int* aOutputFlags, michael@0: int64_t aTimeOut); michael@0: /* michael@0: * Get the codec type michael@0: */ michael@0: int GetCodecType() { return mCodecType; } michael@0: protected: michael@0: /** michael@0: * See whether the object has been initialized successfully and is ready to michael@0: * use. michael@0: */ michael@0: virtual bool IsValid() { return mCodec != nullptr; } michael@0: michael@0: /** michael@0: * Construct codec specific configuration blob with given data aData generated michael@0: * by media codec and append it into aOutputBuf. Needed by MP4 container michael@0: * writer for generating decoder config box, or WebRTC for generating RTP michael@0: * packets. Returns OK if succeed. michael@0: */ michael@0: virtual status_t AppendDecoderConfig(nsTArray* aOutputBuf, michael@0: ABuffer* aData) = 0; michael@0: michael@0: /** michael@0: * Append encoded frame data generated by media codec (stored in aData and michael@0: * is aSize bytes long) into aOutputBuf. Subclasses can override this function michael@0: * to process the data for specific container writer. michael@0: */ michael@0: virtual void AppendFrame(nsTArray* aOutputBuf, michael@0: const uint8_t* aData, size_t aSize) michael@0: { michael@0: aOutputBuf->AppendElements(aData, aSize); michael@0: } michael@0: michael@0: private: michael@0: // Hide these. User should always use creator functions to get a media codec. michael@0: OMXCodecWrapper() MOZ_DELETE; michael@0: OMXCodecWrapper(const OMXCodecWrapper&) MOZ_DELETE; michael@0: OMXCodecWrapper& operator=(const OMXCodecWrapper&) MOZ_DELETE; michael@0: michael@0: /** michael@0: * Create a media codec of given type. It will be a AVC/H.264 video encoder if michael@0: * aCodecType is CODEC_AVC_ENC, or AAC audio encoder if aCodecType is michael@0: * CODEC_AAC_ENC. michael@0: */ michael@0: OMXCodecWrapper(CodecType aCodecType); michael@0: michael@0: // For subclasses to access hidden constructor and implementation details. michael@0: friend class OMXAudioEncoder; michael@0: friend class OMXVideoEncoder; michael@0: michael@0: /** michael@0: * Start the media codec. michael@0: */ michael@0: status_t Start(); michael@0: michael@0: /** michael@0: * Stop the media codec. michael@0: */ michael@0: status_t Stop(); michael@0: michael@0: // The actual codec instance provided by libstagefright. michael@0: sp mCodec; michael@0: michael@0: // A dedicate message loop with its own thread used by MediaCodec. michael@0: sp mLooper; michael@0: michael@0: Vector > mInputBufs; // MediaCodec buffers to hold input data. michael@0: Vector > mOutputBufs; // MediaCodec buffers to hold output data. michael@0: michael@0: int mCodecType; michael@0: bool mStarted; // Has MediaCodec been started? michael@0: bool mAMRCSDProvided; michael@0: }; michael@0: michael@0: /** michael@0: * Audio encoder. michael@0: */ michael@0: class OMXAudioEncoder MOZ_FINAL : public OMXCodecWrapper michael@0: { michael@0: public: michael@0: /** michael@0: * Configure audio codec parameters and start media codec. It must be called michael@0: * before calling Encode() and GetNextEncodedFrame(). michael@0: * aReSamplingRate = 0 means no resampler required michael@0: */ michael@0: nsresult Configure(int aChannelCount, int aInputSampleRate, int aEncodedSampleRate); michael@0: michael@0: /** michael@0: * Encode 16-bit PCM audio samples stored in aSegment. To notify end of michael@0: * stream, set aInputFlags to BUFFER_EOS. Since encoder has limited buffers, michael@0: * this function might not be able to encode all chunks in one call, however michael@0: * it will remove chunks it consumes from aSegment. michael@0: */ michael@0: nsresult Encode(mozilla::AudioSegment& aSegment, int aInputFlags = 0); michael@0: michael@0: ~OMXAudioEncoder(); michael@0: protected: michael@0: virtual status_t AppendDecoderConfig(nsTArray* aOutputBuf, michael@0: ABuffer* aData) MOZ_OVERRIDE; michael@0: private: michael@0: // Hide these. User should always use creator functions to get a media codec. michael@0: OMXAudioEncoder() MOZ_DELETE; michael@0: OMXAudioEncoder(const OMXAudioEncoder&) MOZ_DELETE; michael@0: OMXAudioEncoder& operator=(const OMXAudioEncoder&) MOZ_DELETE; michael@0: michael@0: /** michael@0: * Create a audio codec. It will be a AAC encoder if aCodecType is michael@0: * CODEC_AAC_ENC. michael@0: */ michael@0: OMXAudioEncoder(CodecType aCodecType) michael@0: : OMXCodecWrapper(aCodecType) michael@0: , mResampler(nullptr) michael@0: , mChannels(0) michael@0: , mTimestamp(0) michael@0: , mSampleDuration(0) michael@0: , mResamplingRatio(0) {} michael@0: michael@0: // For creator function to access hidden constructor. michael@0: friend class OMXCodecWrapper; michael@0: michael@0: /** michael@0: * If the input sample rate does not divide 48kHz evenly, the input data are michael@0: * resampled. michael@0: */ michael@0: SpeexResamplerState* mResampler; michael@0: // Number of audio channels. michael@0: size_t mChannels; michael@0: michael@0: float mResamplingRatio; michael@0: // The total duration of audio samples that have been encoded in microseconds. michael@0: int64_t mTimestamp; michael@0: // Time per audio sample in microseconds. michael@0: int64_t mSampleDuration; michael@0: }; michael@0: michael@0: /** michael@0: * Video encoder. michael@0: */ michael@0: class OMXVideoEncoder MOZ_FINAL : public OMXCodecWrapper michael@0: { michael@0: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OMXVideoEncoder) michael@0: public: michael@0: // Types of output blob format. michael@0: enum BlobFormat { michael@0: AVC_MP4, // MP4 file config descripter (defined in ISO/IEC 14496-15 5.2.4.1.1) michael@0: AVC_NAL // NAL (Network Abstract Layer) (defined in ITU-T H.264 7.4.1) michael@0: }; michael@0: michael@0: /** michael@0: * Configure video codec parameters and start media codec. It must be called michael@0: * before calling Encode() and GetNextEncodedFrame(). michael@0: * aBlobFormat specifies output blob format provided by encoder. It can be michael@0: * AVC_MP4 or AVC_NAL. michael@0: */ michael@0: nsresult Configure(int aWidth, int aHeight, int aFrameRate, michael@0: BlobFormat aBlobFormat = BlobFormat::AVC_MP4); michael@0: michael@0: /** michael@0: * Encode a aWidth pixels wide and aHeight pixels tall video frame of michael@0: * semi-planar YUV420 format stored in the buffer of aImage. aTimestamp gives michael@0: * the frame timestamp/presentation time (in microseconds). To notify end of michael@0: * stream, set aInputFlags to BUFFER_EOS. michael@0: */ michael@0: nsresult Encode(const mozilla::layers::Image* aImage, int aWidth, int aHeight, michael@0: int64_t aTimestamp, int aInputFlags = 0); michael@0: michael@0: #if ANDROID_VERSION >= 18 michael@0: /** Set encoding bitrate (in kbps). */ michael@0: nsresult SetBitrate(int32_t aKbps); michael@0: #endif michael@0: michael@0: /** michael@0: * Get current AVC codec config blob. The output format depends on the michael@0: * aBlobFormat argument given when Configure() was called. michael@0: */ michael@0: nsresult GetCodecConfig(nsTArray* aOutputBuf); michael@0: michael@0: protected: michael@0: virtual status_t AppendDecoderConfig(nsTArray* aOutputBuf, michael@0: ABuffer* aData) MOZ_OVERRIDE; michael@0: michael@0: // If configured to output MP4 format blob, AVC/H.264 encoder has to replace michael@0: // NAL unit start code with the unit length as specified in michael@0: // ISO/IEC 14496-15 5.2.3. michael@0: virtual void AppendFrame(nsTArray* aOutputBuf, michael@0: const uint8_t* aData, size_t aSize) MOZ_OVERRIDE; michael@0: michael@0: private: michael@0: // Hide these. User should always use creator functions to get a media codec. michael@0: OMXVideoEncoder() MOZ_DELETE; michael@0: OMXVideoEncoder(const OMXVideoEncoder&) MOZ_DELETE; michael@0: OMXVideoEncoder& operator=(const OMXVideoEncoder&) MOZ_DELETE; michael@0: michael@0: /** michael@0: * Create a video codec. It will be a AVC/H.264 encoder if aCodecType is michael@0: * CODEC_AVC_ENC. michael@0: */ michael@0: OMXVideoEncoder(CodecType aCodecType) michael@0: : OMXCodecWrapper(aCodecType) michael@0: , mWidth(0) michael@0: , mHeight(0) michael@0: , mBlobFormat(BlobFormat::AVC_MP4) michael@0: , mHasConfigBlob(false) michael@0: {} michael@0: michael@0: // For creator function to access hidden constructor. michael@0: friend class OMXCodecWrapper; michael@0: michael@0: int mWidth; michael@0: int mHeight; michael@0: BlobFormat mBlobFormat; michael@0: bool mHasConfigBlob; michael@0: }; michael@0: michael@0: } // namespace android michael@0: #endif // OMXCodecWrapper_h_