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