Loading media/codec2/components/hevc/C2SoftHevcEnc.cpp +99 −5 Original line number Original line Diff line number Diff line Loading @@ -42,6 +42,36 @@ namespace { constexpr char COMPONENT_NAME[] = "c2.android.hevc.encoder"; constexpr char COMPONENT_NAME[] = "c2.android.hevc.encoder"; void ParseGop( const C2StreamGopTuning::output &gop, uint32_t *syncInterval, uint32_t *iInterval, uint32_t *maxBframes) { uint32_t syncInt = 1; uint32_t iInt = 1; for (size_t i = 0; i < gop.flexCount(); ++i) { const C2GopLayerStruct &layer = gop.m.values[i]; if (layer.count == UINT32_MAX) { syncInt = 0; } else if (syncInt <= UINT32_MAX / (layer.count + 1)) { syncInt *= (layer.count + 1); } if ((layer.type_ & I_FRAME) == 0) { if (layer.count == UINT32_MAX) { iInt = 0; } else if (iInt <= UINT32_MAX / (layer.count + 1)) { iInt *= (layer.count + 1); } } if (layer.type_ == C2Config::picture_type_t(P_FRAME | B_FRAME) && maxBframes) { *maxBframes = layer.count; } } if (syncInterval) { *syncInterval = syncInt; } if (iInterval) { *iInterval = iInt; } } } // namepsace } // namepsace class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams { class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams { Loading @@ -59,14 +89,22 @@ class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams { noTimeStretch(); noTimeStretch(); setDerivedInstance(this); setDerivedInstance(this); addParameter( DefineParam(mGop, C2_PARAMKEY_GOP) .withDefault(C2StreamGopTuning::output::AllocShared( 0 /* flexCount */, 0u /* stream */)) .withFields({C2F(mGop, m.values[0].type_).any(), C2F(mGop, m.values[0].count).any()}) .withSetter(GopSetter) .build()); addParameter( addParameter( DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY) DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY) .withDefault(new C2PortActualDelayTuning::input( .withDefault(new C2PortActualDelayTuning::input( DEFAULT_B_FRAMES + DEFAULT_RC_LOOKAHEAD)) DEFAULT_B_FRAMES + DEFAULT_RC_LOOKAHEAD)) .withFields({C2F(mActualInputDelay, value).inRange( .withFields({C2F(mActualInputDelay, value).inRange( 0, MAX_B_FRAMES + MAX_RC_LOOKAHEAD)}) 0, MAX_B_FRAMES + MAX_RC_LOOKAHEAD)}) .withSetter( .calculatedAs(InputDelaySetter, mGop) Setter<decltype(*mActualInputDelay)>::StrictValueWithNoDeps) .build()); .build()); addParameter( addParameter( Loading Loading @@ -172,6 +210,17 @@ class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams { .build()); .build()); } } static C2R InputDelaySetter( bool mayBlock, C2P<C2PortActualDelayTuning::input> &me, const C2P<C2StreamGopTuning::output> &gop) { (void)mayBlock; uint32_t maxBframes = 0; ParseGop(gop.v, nullptr, nullptr, &maxBframes); me.set().value = maxBframes + DEFAULT_RC_LOOKAHEAD; return C2R::Ok(); } static C2R BitrateSetter(bool mayBlock, static C2R BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output>& me) { C2P<C2StreamBitrateInfo::output>& me) { (void)mayBlock; (void)mayBlock; Loading Loading @@ -270,6 +319,18 @@ class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams { return C2R::Ok(); return C2R::Ok(); } } static C2R GopSetter(bool mayBlock, C2P<C2StreamGopTuning::output> &me) { (void)mayBlock; for (size_t i = 0; i < me.v.flexCount(); ++i) { const C2GopLayerStruct &layer = me.v.m.values[0]; if (layer.type_ == C2Config::picture_type_t(P_FRAME | B_FRAME) && layer.count > MAX_B_FRAMES) { me.set().m.values[i].count = MAX_B_FRAMES; } } return C2R::Ok(); } UWORD32 getProfile_l() const { UWORD32 getProfile_l() const { switch (mProfileLevel->profile) { switch (mProfileLevel->profile) { case PROFILE_HEVC_MAIN: [[fallthrough]]; case PROFILE_HEVC_MAIN: [[fallthrough]]; Loading Loading @@ -338,6 +399,9 @@ class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams { std::shared_ptr<C2StreamQualityTuning::output> getQuality_l() const { std::shared_ptr<C2StreamQualityTuning::output> getQuality_l() const { return mQuality; return mQuality; } } std::shared_ptr<C2StreamGopTuning::output> getGop_l() const { return mGop; } private: private: std::shared_ptr<C2StreamUsageTuning::input> mUsage; std::shared_ptr<C2StreamUsageTuning::input> mUsage; Loading @@ -350,6 +414,7 @@ class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams { std::shared_ptr<C2StreamQualityTuning::output> mQuality; std::shared_ptr<C2StreamQualityTuning::output> mQuality; std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel; std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel; std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod; std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod; std::shared_ptr<C2StreamGopTuning::output> mGop; }; }; static size_t GetCPUCoreCount() { static size_t GetCPUCoreCount() { Loading Loading @@ -449,7 +514,25 @@ c2_status_t C2SoftHevcEnc::initEncParams() { ALOGE("HEVC default init failed : 0x%x", err); ALOGE("HEVC default init failed : 0x%x", err); return C2_CORRUPTED; return C2_CORRUPTED; } } mBframes = 0; if (mGop && mGop->flexCount() > 0) { uint32_t syncInterval = 1; uint32_t iInterval = 1; uint32_t maxBframes = 0; ParseGop(*mGop, &syncInterval, &iInterval, &maxBframes); if (syncInterval > 0) { ALOGD("Updating IDR interval from GOP: old %u new %u", mIDRInterval, syncInterval); mIDRInterval = syncInterval; } if (iInterval > 0) { ALOGD("Updating I interval from GOP: old %u new %u", mIInterval, iInterval); mIInterval = iInterval; } if (mBframes != maxBframes) { ALOGD("Updating max B frames from GOP: old %u new %u", mBframes, maxBframes); mBframes = maxBframes; } } // update configuration // update configuration mEncParams.s_src_prms.i4_width = mSize->width; mEncParams.s_src_prms.i4_width = mSize->width; mEncParams.s_src_prms.i4_height = mSize->height; mEncParams.s_src_prms.i4_height = mSize->height; Loading @@ -463,12 +546,20 @@ c2_status_t C2SoftHevcEnc::initEncParams() { mBitrate->value << 1; mBitrate->value << 1; mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_codec_level = mHevcEncLevel; mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_codec_level = mHevcEncLevel; mEncParams.s_coding_tools_prms.i4_max_i_open_gop_period = mIDRInterval; mEncParams.s_coding_tools_prms.i4_max_i_open_gop_period = mIDRInterval; mEncParams.s_coding_tools_prms.i4_max_cra_open_gop_period = mIDRInterval; mEncParams.s_coding_tools_prms.i4_max_cra_open_gop_period = mIInterval; mIvVideoColorFormat = IV_YUV_420P; mIvVideoColorFormat = IV_YUV_420P; mEncParams.s_multi_thrd_prms.i4_max_num_cores = mNumCores; mEncParams.s_multi_thrd_prms.i4_max_num_cores = mNumCores; mEncParams.s_out_strm_prms.i4_codec_profile = mHevcEncProfile; mEncParams.s_out_strm_prms.i4_codec_profile = mHevcEncProfile; mEncParams.s_lap_prms.i4_rc_look_ahead_pics = DEFAULT_RC_LOOKAHEAD; mEncParams.s_lap_prms.i4_rc_look_ahead_pics = DEFAULT_RC_LOOKAHEAD; mEncParams.s_coding_tools_prms.i4_max_temporal_layers = DEFAULT_B_FRAMES; if (mBframes == 0) { mEncParams.s_coding_tools_prms.i4_max_temporal_layers = 0; } else if (mBframes <= 2) { mEncParams.s_coding_tools_prms.i4_max_temporal_layers = 1; } else if (mBframes <= 6) { mEncParams.s_coding_tools_prms.i4_max_temporal_layers = 2; } else { mEncParams.s_coding_tools_prms.i4_max_temporal_layers = 3; } switch (mBitrateMode->value) { switch (mBitrateMode->value) { case C2Config::BITRATE_IGNORE: case C2Config::BITRATE_IGNORE: Loading Loading @@ -523,6 +614,7 @@ c2_status_t C2SoftHevcEnc::drain(uint32_t drainMode, c2_status_t C2SoftHevcEnc::initEncoder() { c2_status_t C2SoftHevcEnc::initEncoder() { CHECK(!mCodecCtx); CHECK(!mCodecCtx); { { IntfImpl::Lock lock = mIntf->lock(); IntfImpl::Lock lock = mIntf->lock(); mSize = mIntf->getSize_l(); mSize = mIntf->getSize_l(); Loading @@ -532,8 +624,10 @@ c2_status_t C2SoftHevcEnc::initEncoder() { mHevcEncProfile = mIntf->getProfile_l(); mHevcEncProfile = mIntf->getProfile_l(); mHevcEncLevel = mIntf->getLevel_l(); mHevcEncLevel = mIntf->getLevel_l(); mIDRInterval = mIntf->getSyncFramePeriod_l(); mIDRInterval = mIntf->getSyncFramePeriod_l(); mIInterval = mIntf->getSyncFramePeriod_l(); mComplexity = mIntf->getComplexity_l(); mComplexity = mIntf->getComplexity_l(); mQuality = mIntf->getQuality_l(); mQuality = mIntf->getQuality_l(); mGop = mIntf->getGop_l(); } } c2_status_t status = initEncParams(); c2_status_t status = initEncParams(); Loading media/codec2/components/hevc/C2SoftHevcEnc.h +3 −1 Original line number Original line Diff line number Diff line Loading @@ -67,6 +67,8 @@ struct C2SoftHevcEnc : public SimpleC2Component { ihevce_static_cfg_params_t mEncParams; ihevce_static_cfg_params_t mEncParams; size_t mNumCores; size_t mNumCores; UWORD32 mIDRInterval; UWORD32 mIDRInterval; UWORD32 mIInterval; UWORD32 mBframes; IV_COLOR_FORMAT_T mIvVideoColorFormat; IV_COLOR_FORMAT_T mIvVideoColorFormat; UWORD32 mHevcEncProfile; UWORD32 mHevcEncProfile; UWORD32 mHevcEncLevel; UWORD32 mHevcEncLevel; Loading @@ -85,7 +87,7 @@ struct C2SoftHevcEnc : public SimpleC2Component { std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode; std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode; std::shared_ptr<C2StreamComplexityTuning::output> mComplexity; std::shared_ptr<C2StreamComplexityTuning::output> mComplexity; std::shared_ptr<C2StreamQualityTuning::output> mQuality; std::shared_ptr<C2StreamQualityTuning::output> mQuality; std::shared_ptr<C2StreamGopTuning::output> mGop; #ifdef FILE_DUMP_ENABLE #ifdef FILE_DUMP_ENABLE char mInFile[200]; char mInFile[200]; char mOutFile[200]; char mOutFile[200]; Loading Loading
media/codec2/components/hevc/C2SoftHevcEnc.cpp +99 −5 Original line number Original line Diff line number Diff line Loading @@ -42,6 +42,36 @@ namespace { constexpr char COMPONENT_NAME[] = "c2.android.hevc.encoder"; constexpr char COMPONENT_NAME[] = "c2.android.hevc.encoder"; void ParseGop( const C2StreamGopTuning::output &gop, uint32_t *syncInterval, uint32_t *iInterval, uint32_t *maxBframes) { uint32_t syncInt = 1; uint32_t iInt = 1; for (size_t i = 0; i < gop.flexCount(); ++i) { const C2GopLayerStruct &layer = gop.m.values[i]; if (layer.count == UINT32_MAX) { syncInt = 0; } else if (syncInt <= UINT32_MAX / (layer.count + 1)) { syncInt *= (layer.count + 1); } if ((layer.type_ & I_FRAME) == 0) { if (layer.count == UINT32_MAX) { iInt = 0; } else if (iInt <= UINT32_MAX / (layer.count + 1)) { iInt *= (layer.count + 1); } } if (layer.type_ == C2Config::picture_type_t(P_FRAME | B_FRAME) && maxBframes) { *maxBframes = layer.count; } } if (syncInterval) { *syncInterval = syncInt; } if (iInterval) { *iInterval = iInt; } } } // namepsace } // namepsace class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams { class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams { Loading @@ -59,14 +89,22 @@ class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams { noTimeStretch(); noTimeStretch(); setDerivedInstance(this); setDerivedInstance(this); addParameter( DefineParam(mGop, C2_PARAMKEY_GOP) .withDefault(C2StreamGopTuning::output::AllocShared( 0 /* flexCount */, 0u /* stream */)) .withFields({C2F(mGop, m.values[0].type_).any(), C2F(mGop, m.values[0].count).any()}) .withSetter(GopSetter) .build()); addParameter( addParameter( DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY) DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY) .withDefault(new C2PortActualDelayTuning::input( .withDefault(new C2PortActualDelayTuning::input( DEFAULT_B_FRAMES + DEFAULT_RC_LOOKAHEAD)) DEFAULT_B_FRAMES + DEFAULT_RC_LOOKAHEAD)) .withFields({C2F(mActualInputDelay, value).inRange( .withFields({C2F(mActualInputDelay, value).inRange( 0, MAX_B_FRAMES + MAX_RC_LOOKAHEAD)}) 0, MAX_B_FRAMES + MAX_RC_LOOKAHEAD)}) .withSetter( .calculatedAs(InputDelaySetter, mGop) Setter<decltype(*mActualInputDelay)>::StrictValueWithNoDeps) .build()); .build()); addParameter( addParameter( Loading Loading @@ -172,6 +210,17 @@ class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams { .build()); .build()); } } static C2R InputDelaySetter( bool mayBlock, C2P<C2PortActualDelayTuning::input> &me, const C2P<C2StreamGopTuning::output> &gop) { (void)mayBlock; uint32_t maxBframes = 0; ParseGop(gop.v, nullptr, nullptr, &maxBframes); me.set().value = maxBframes + DEFAULT_RC_LOOKAHEAD; return C2R::Ok(); } static C2R BitrateSetter(bool mayBlock, static C2R BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output>& me) { C2P<C2StreamBitrateInfo::output>& me) { (void)mayBlock; (void)mayBlock; Loading Loading @@ -270,6 +319,18 @@ class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams { return C2R::Ok(); return C2R::Ok(); } } static C2R GopSetter(bool mayBlock, C2P<C2StreamGopTuning::output> &me) { (void)mayBlock; for (size_t i = 0; i < me.v.flexCount(); ++i) { const C2GopLayerStruct &layer = me.v.m.values[0]; if (layer.type_ == C2Config::picture_type_t(P_FRAME | B_FRAME) && layer.count > MAX_B_FRAMES) { me.set().m.values[i].count = MAX_B_FRAMES; } } return C2R::Ok(); } UWORD32 getProfile_l() const { UWORD32 getProfile_l() const { switch (mProfileLevel->profile) { switch (mProfileLevel->profile) { case PROFILE_HEVC_MAIN: [[fallthrough]]; case PROFILE_HEVC_MAIN: [[fallthrough]]; Loading Loading @@ -338,6 +399,9 @@ class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams { std::shared_ptr<C2StreamQualityTuning::output> getQuality_l() const { std::shared_ptr<C2StreamQualityTuning::output> getQuality_l() const { return mQuality; return mQuality; } } std::shared_ptr<C2StreamGopTuning::output> getGop_l() const { return mGop; } private: private: std::shared_ptr<C2StreamUsageTuning::input> mUsage; std::shared_ptr<C2StreamUsageTuning::input> mUsage; Loading @@ -350,6 +414,7 @@ class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams { std::shared_ptr<C2StreamQualityTuning::output> mQuality; std::shared_ptr<C2StreamQualityTuning::output> mQuality; std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel; std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel; std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod; std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod; std::shared_ptr<C2StreamGopTuning::output> mGop; }; }; static size_t GetCPUCoreCount() { static size_t GetCPUCoreCount() { Loading Loading @@ -449,7 +514,25 @@ c2_status_t C2SoftHevcEnc::initEncParams() { ALOGE("HEVC default init failed : 0x%x", err); ALOGE("HEVC default init failed : 0x%x", err); return C2_CORRUPTED; return C2_CORRUPTED; } } mBframes = 0; if (mGop && mGop->flexCount() > 0) { uint32_t syncInterval = 1; uint32_t iInterval = 1; uint32_t maxBframes = 0; ParseGop(*mGop, &syncInterval, &iInterval, &maxBframes); if (syncInterval > 0) { ALOGD("Updating IDR interval from GOP: old %u new %u", mIDRInterval, syncInterval); mIDRInterval = syncInterval; } if (iInterval > 0) { ALOGD("Updating I interval from GOP: old %u new %u", mIInterval, iInterval); mIInterval = iInterval; } if (mBframes != maxBframes) { ALOGD("Updating max B frames from GOP: old %u new %u", mBframes, maxBframes); mBframes = maxBframes; } } // update configuration // update configuration mEncParams.s_src_prms.i4_width = mSize->width; mEncParams.s_src_prms.i4_width = mSize->width; mEncParams.s_src_prms.i4_height = mSize->height; mEncParams.s_src_prms.i4_height = mSize->height; Loading @@ -463,12 +546,20 @@ c2_status_t C2SoftHevcEnc::initEncParams() { mBitrate->value << 1; mBitrate->value << 1; mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_codec_level = mHevcEncLevel; mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_codec_level = mHevcEncLevel; mEncParams.s_coding_tools_prms.i4_max_i_open_gop_period = mIDRInterval; mEncParams.s_coding_tools_prms.i4_max_i_open_gop_period = mIDRInterval; mEncParams.s_coding_tools_prms.i4_max_cra_open_gop_period = mIDRInterval; mEncParams.s_coding_tools_prms.i4_max_cra_open_gop_period = mIInterval; mIvVideoColorFormat = IV_YUV_420P; mIvVideoColorFormat = IV_YUV_420P; mEncParams.s_multi_thrd_prms.i4_max_num_cores = mNumCores; mEncParams.s_multi_thrd_prms.i4_max_num_cores = mNumCores; mEncParams.s_out_strm_prms.i4_codec_profile = mHevcEncProfile; mEncParams.s_out_strm_prms.i4_codec_profile = mHevcEncProfile; mEncParams.s_lap_prms.i4_rc_look_ahead_pics = DEFAULT_RC_LOOKAHEAD; mEncParams.s_lap_prms.i4_rc_look_ahead_pics = DEFAULT_RC_LOOKAHEAD; mEncParams.s_coding_tools_prms.i4_max_temporal_layers = DEFAULT_B_FRAMES; if (mBframes == 0) { mEncParams.s_coding_tools_prms.i4_max_temporal_layers = 0; } else if (mBframes <= 2) { mEncParams.s_coding_tools_prms.i4_max_temporal_layers = 1; } else if (mBframes <= 6) { mEncParams.s_coding_tools_prms.i4_max_temporal_layers = 2; } else { mEncParams.s_coding_tools_prms.i4_max_temporal_layers = 3; } switch (mBitrateMode->value) { switch (mBitrateMode->value) { case C2Config::BITRATE_IGNORE: case C2Config::BITRATE_IGNORE: Loading Loading @@ -523,6 +614,7 @@ c2_status_t C2SoftHevcEnc::drain(uint32_t drainMode, c2_status_t C2SoftHevcEnc::initEncoder() { c2_status_t C2SoftHevcEnc::initEncoder() { CHECK(!mCodecCtx); CHECK(!mCodecCtx); { { IntfImpl::Lock lock = mIntf->lock(); IntfImpl::Lock lock = mIntf->lock(); mSize = mIntf->getSize_l(); mSize = mIntf->getSize_l(); Loading @@ -532,8 +624,10 @@ c2_status_t C2SoftHevcEnc::initEncoder() { mHevcEncProfile = mIntf->getProfile_l(); mHevcEncProfile = mIntf->getProfile_l(); mHevcEncLevel = mIntf->getLevel_l(); mHevcEncLevel = mIntf->getLevel_l(); mIDRInterval = mIntf->getSyncFramePeriod_l(); mIDRInterval = mIntf->getSyncFramePeriod_l(); mIInterval = mIntf->getSyncFramePeriod_l(); mComplexity = mIntf->getComplexity_l(); mComplexity = mIntf->getComplexity_l(); mQuality = mIntf->getQuality_l(); mQuality = mIntf->getQuality_l(); mGop = mIntf->getGop_l(); } } c2_status_t status = initEncParams(); c2_status_t status = initEncParams(); Loading
media/codec2/components/hevc/C2SoftHevcEnc.h +3 −1 Original line number Original line Diff line number Diff line Loading @@ -67,6 +67,8 @@ struct C2SoftHevcEnc : public SimpleC2Component { ihevce_static_cfg_params_t mEncParams; ihevce_static_cfg_params_t mEncParams; size_t mNumCores; size_t mNumCores; UWORD32 mIDRInterval; UWORD32 mIDRInterval; UWORD32 mIInterval; UWORD32 mBframes; IV_COLOR_FORMAT_T mIvVideoColorFormat; IV_COLOR_FORMAT_T mIvVideoColorFormat; UWORD32 mHevcEncProfile; UWORD32 mHevcEncProfile; UWORD32 mHevcEncLevel; UWORD32 mHevcEncLevel; Loading @@ -85,7 +87,7 @@ struct C2SoftHevcEnc : public SimpleC2Component { std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode; std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode; std::shared_ptr<C2StreamComplexityTuning::output> mComplexity; std::shared_ptr<C2StreamComplexityTuning::output> mComplexity; std::shared_ptr<C2StreamQualityTuning::output> mQuality; std::shared_ptr<C2StreamQualityTuning::output> mQuality; std::shared_ptr<C2StreamGopTuning::output> mGop; #ifdef FILE_DUMP_ENABLE #ifdef FILE_DUMP_ENABLE char mInFile[200]; char mInFile[200]; char mOutFile[200]; char mOutFile[200]; Loading