/* * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_C2_SOFT_VPX_ENC_H__ #define ANDROID_C2_SOFT_VPX_ENC_H__ #include #include #include #include #include #include #include "vpx/vpx_encoder.h" #include "vpx/vpx_codec.h" #include "vpx/vpx_image.h" #include "vpx/vp8cx.h" namespace android { // TODO: These defs taken from deprecated OMX_VideoExt.h. Move these definitions // to a new header file and include it. /** Maximum number of temporal layers */ #define MAXTEMPORALLAYERS 3 /** temporal layer patterns */ typedef enum TemporalPatternType { VPXTemporalLayerPatternNone = 0, VPXTemporalLayerPatternWebRTC = 1, VPXTemporalLayerPatternMax = 0x7FFFFFFF } TemporalPatternType; // Base class for a VPX Encoder Component // // Only following encoder settings are available (codec specific settings might // be available in the sub-classes): // - video resolution // - target bitrate // - rate control (constant / variable) // - frame rate // - error resilience // - reconstruction & loop filters (g_profile) // // Only following color formats are recognized // - C2PlanarLayout::TYPE_RGB // - C2PlanarLayout::TYPE_RGBA // // Following settings are not configurable by the client // - encoding deadline is realtime // - multithreaded encoding utilizes a number of threads equal // to online cpu's available // - the algorithm interface for encoder is decided by the sub-class in use // - fractional bits of frame rate is discarded // - timestamps are in microseconds, therefore encoder timebase is fixed // to 1/1000000 struct C2SoftVpxEnc : public SimpleC2Component { class IntfImpl; C2SoftVpxEnc(const char* name, c2_node_id_t id, const std::shared_ptr& intfImpl); // From SimpleC2Component c2_status_t onInit() override final; c2_status_t onStop() override final; void onReset() override final; void onRelease() override final; c2_status_t onFlush_sm() override final; void process( const std::unique_ptr &work, const std::shared_ptr &pool) override final; c2_status_t drain( uint32_t drainMode, const std::shared_ptr &pool) override final; protected: std::shared_ptr mIntf; virtual ~C2SoftVpxEnc(); // Initializes vpx encoder with available settings. status_t initEncoder(); // Populates mCodecInterface with codec specific settings. virtual void setCodecSpecificInterface() = 0; // Sets codec specific configuration. virtual void setCodecSpecificConfiguration() = 0; // Sets codec specific encoder controls. virtual vpx_codec_err_t setCodecSpecificControls() = 0; // Get current encode flags. virtual vpx_enc_frame_flags_t getEncodeFlags(); enum TemporalReferences { // For 1 layer case: reference all (last, golden, and alt ref), but only // update last. kTemporalUpdateLastRefAll = 12, // First base layer frame for 3 temporal layers, which updates last and // golden with alt ref dependency. kTemporalUpdateLastAndGoldenRefAltRef = 11, // First enhancement layer with alt ref dependency. kTemporalUpdateGoldenRefAltRef = 10, // First enhancement layer with alt ref dependency. kTemporalUpdateGoldenWithoutDependencyRefAltRef = 9, // Base layer with alt ref dependency. kTemporalUpdateLastRefAltRef = 8, // Highest enhacement layer without dependency on golden with alt ref // dependency. kTemporalUpdateNoneNoRefGoldenRefAltRef = 7, // Second layer and last frame in cycle, for 2 layers. kTemporalUpdateNoneNoRefAltref = 6, // Highest enhancement layer. kTemporalUpdateNone = 5, // Second enhancement layer. kTemporalUpdateAltref = 4, // Second enhancement layer without dependency on previous frames in // the second enhancement layer. kTemporalUpdateAltrefWithoutDependency = 3, // First enhancement layer. kTemporalUpdateGolden = 2, // First enhancement layer without dependency on previous frames in // the first enhancement layer. kTemporalUpdateGoldenWithoutDependency = 1, // Base layer. kTemporalUpdateLast = 0, }; enum { kMaxTemporalPattern = 8 }; // vpx specific opaque data structure that // stores encoder state vpx_codec_ctx_t* mCodecContext; // vpx specific data structure that // stores encoder configuration vpx_codec_enc_cfg_t* mCodecConfiguration; // vpx specific read-only data structure // that specifies algorithm interface (e.g. vp8) vpx_codec_iface_t* mCodecInterface; // align stride to the power of 2 int32_t mStrideAlign; // Color format for the input port vpx_img_fmt_t mColorFormat; // Bitrate control mode, either constant or variable vpx_rc_mode mBitrateControlMode; // Parameter that denotes whether error resilience // is enabled in encoder bool mErrorResilience; // Minimum (best quality) quantizer uint32_t mMinQuantizer; // Maximum (worst quality) quantizer uint32_t mMaxQuantizer; // Number of coding temporal layers to be used. size_t mTemporalLayers; // Temporal layer bitrare ratio in percentage uint32_t mTemporalLayerBitrateRatio[MAXTEMPORALLAYERS]; // Temporal pattern type TemporalPatternType mTemporalPatternType; // Temporal pattern length size_t mTemporalPatternLength; // Temporal pattern current index size_t mTemporalPatternIdx; // Frame type temporal pattern TemporalReferences mTemporalPattern[kMaxTemporalPattern]; // Last input buffer timestamp uint64_t mLastTimestamp; // Number of input frames int64_t mNumInputFrames; // Conversion buffer is needed to input to // yuv420 planar format. MemoryBlock mConversionBuffer; // Signalled EOS bool mSignalledOutputEos; // Signalled Error bool mSignalledError; // configurations used by component in process // (TODO: keep this in intf but make them internal only) std::shared_ptr mSize; std::shared_ptr mIntraRefresh; std::shared_ptr mFrameRate; std::shared_ptr mBitrate; std::shared_ptr mBitrateMode; std::shared_ptr mRequestSync; C2_DO_NOT_COPY(C2SoftVpxEnc); }; namespace { #ifdef VP9 constexpr char COMPONENT_NAME[] = "c2.android.vp9.encoder"; const char *MEDIA_MIMETYPE_VIDEO = MEDIA_MIMETYPE_VIDEO_VP9; #else constexpr char COMPONENT_NAME[] = "c2.android.vp8.encoder"; const char *MEDIA_MIMETYPE_VIDEO = MEDIA_MIMETYPE_VIDEO_VP8; #endif } // namepsace class C2SoftVpxEnc::IntfImpl : public SimpleInterface::BaseParams { public: explicit IntfImpl(const std::shared_ptr &helper) : SimpleInterface::BaseParams( helper, COMPONENT_NAME, C2Component::KIND_ENCODER, C2Component::DOMAIN_VIDEO, MEDIA_MIMETYPE_VIDEO) { noPrivateBuffers(); // TODO: account for our buffers here noInputReferences(); noOutputReferences(); noInputLatency(); noTimeStretch(); setDerivedInstance(this); addParameter( DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) .withConstValue(new C2ComponentAttributesSetting( C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE) .withConstValue(new C2StreamUsageTuning::input( 0u, (uint64_t)C2MemoryUsage::CPU_READ)) .build()); addParameter( DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE) .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240)) .withFields({ C2F(mSize, width).inRange(2, 2048, 2), C2F(mSize, height).inRange(2, 2048, 2), }) .withSetter(SizeSetter) .build()); addParameter( DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE) .withDefault(new C2StreamBitrateModeTuning::output( 0u, C2Config::BITRATE_VARIABLE)) .withFields({ C2F(mBitrateMode, value).oneOf({ C2Config::BITRATE_CONST, C2Config::BITRATE_VARIABLE }) }) .withSetter( Setter::StrictValueWithNoDeps) .build()); addParameter( DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE) .withDefault(new C2StreamFrameRateInfo::output(0u, 30.)) // TODO: More restriction? .withFields({C2F(mFrameRate, value).greaterThan(0.)}) .withSetter( Setter::StrictValueWithNoDeps) .build()); addParameter( DefineParam(mLayering, C2_PARAMKEY_TEMPORAL_LAYERING) .withDefault(C2StreamTemporalLayeringTuning::output::AllocShared(0u, 0, 0, 0)) .withFields({ C2F(mLayering, m.layerCount).inRange(0, 4), C2F(mLayering, m.bLayerCount).inRange(0, 0), C2F(mLayering, m.bitrateRatios).inRange(0., 1.) }) .withSetter(LayeringSetter) .build()); addParameter( DefineParam(mSyncFramePeriod, C2_PARAMKEY_SYNC_FRAME_INTERVAL) .withDefault(new C2StreamSyncFrameIntervalTuning::output(0u, 1000000)) .withFields({C2F(mSyncFramePeriod, value).any()}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( DefineParam(mBitrate, C2_PARAMKEY_BITRATE) .withDefault(new C2StreamBitrateInfo::output(0u, 64000)) .withFields({C2F(mBitrate, value).inRange(4096, 40000000)}) .withSetter(BitrateSetter) .build()); addParameter( DefineParam(mIntraRefresh, C2_PARAMKEY_INTRA_REFRESH) .withConstValue(new C2StreamIntraRefreshTuning::output( 0u, C2Config::INTRA_REFRESH_DISABLED, 0.)) .build()); addParameter( DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL) .withDefault(new C2StreamProfileLevelInfo::output( 0u, PROFILE_VP9_0, LEVEL_VP9_4_1)) .withFields({ C2F(mProfileLevel, profile).equalTo( PROFILE_VP9_0 ), C2F(mProfileLevel, level).equalTo( LEVEL_VP9_4_1), }) .withSetter(ProfileLevelSetter) .build()); addParameter( DefineParam(mRequestSync, C2_PARAMKEY_REQUEST_SYNC_FRAME) .withDefault(new C2StreamRequestSyncFrameTuning::output(0u, C2_FALSE)) .withFields({C2F(mRequestSync, value).oneOf({ C2_FALSE, C2_TRUE }) }) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); } static C2R BitrateSetter(bool mayBlock, C2P &me) { (void)mayBlock; C2R res = C2R::Ok(); if (me.v.value <= 4096) { me.set().value = 4096; } return res; } static C2R SizeSetter(bool mayBlock, const C2P &oldMe, C2P &me) { (void)mayBlock; C2R res = C2R::Ok(); if (!me.F(me.v.width).supportsAtAll(me.v.width)) { res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width))); me.set().width = oldMe.v.width; } if (!me.F(me.v.height).supportsAtAll(me.v.height)) { res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height))); me.set().height = oldMe.v.height; } return res; } static C2R ProfileLevelSetter( bool mayBlock, C2P &me) { (void)mayBlock; if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) { me.set().profile = PROFILE_VP9_0; } if (!me.F(me.v.level).supportsAtAll(me.v.level)) { me.set().level = LEVEL_VP9_4_1; } return C2R::Ok(); } static C2R LayeringSetter(bool mayBlock, C2P& me) { (void)mayBlock; C2R res = C2R::Ok(); if (me.v.m.layerCount > 4) { me.set().m.layerCount = 4; } me.set().m.bLayerCount = 0; // ensure ratios are monotonic and clamped between 0 and 1 for (size_t ix = 0; ix < me.v.flexCount(); ++ix) { me.set().m.bitrateRatios[ix] = c2_clamp( ix > 0 ? me.v.m.bitrateRatios[ix - 1] : 0, me.v.m.bitrateRatios[ix], 1.); } ALOGI("setting temporal layering %u + %u", me.v.m.layerCount, me.v.m.bLayerCount); return res; } // unsafe getters std::shared_ptr getSize_l() const { return mSize; } std::shared_ptr getIntraRefresh_l() const { return mIntraRefresh; } std::shared_ptr getFrameRate_l() const { return mFrameRate; } std::shared_ptr getBitrate_l() const { return mBitrate; } std::shared_ptr getBitrateMode_l() const { return mBitrateMode; } std::shared_ptr getRequestSync_l() const { return mRequestSync; } std::shared_ptr getTemporalLayers_l() const { return mLayering; } uint32_t getSyncFramePeriod() const { if (mSyncFramePeriod->value < 0 || mSyncFramePeriod->value == INT64_MAX) { return 0; } double period = mSyncFramePeriod->value / 1e6 * mFrameRate->value; return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.); } private: std::shared_ptr mUsage; std::shared_ptr mSize; std::shared_ptr mFrameRate; std::shared_ptr mLayering; std::shared_ptr mIntraRefresh; std::shared_ptr mRequestSync; std::shared_ptr mSyncFramePeriod; std::shared_ptr mBitrate; std::shared_ptr mBitrateMode; std::shared_ptr mProfileLevel; }; } // namespace android #endif // ANDROID_C2_SOFT_VPX_ENC_H__