1 /*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "tester_codecbase.h"
17 #include "avcodec_errors.h"
18 #include "type_converter.h"
19 #include "hcodec_log.h"
20 #include "hcodec_api.h"
21
22 namespace OHOS::MediaAVCodec {
23 using namespace std;
24
OnError(AVCodecErrorType errorType,int32_t errorCode)25 void TesterCodecBase::CallBack::OnError(AVCodecErrorType errorType, int32_t errorCode)
26 {
27 TLOGI(">>");
28 }
29
OnOutputFormatChanged(const Format & format)30 void TesterCodecBase::CallBack::OnOutputFormatChanged(const Format &format)
31 {
32 TLOGI(">>");
33 }
34
OnInputBufferAvailable(uint32_t index,std::shared_ptr<AVBuffer> buffer)35 void TesterCodecBase::CallBack::OnInputBufferAvailable(uint32_t index, std::shared_ptr<AVBuffer> buffer)
36 {
37 lock_guard<mutex> lk(tester_->inputMtx_);
38 tester_->inputList_.emplace_back(index, buffer);
39 tester_->inputCond_.notify_all();
40 }
41
OnOutputBufferAvailable(uint32_t index,std::shared_ptr<AVBuffer> buffer)42 void TesterCodecBase::CallBack::OnOutputBufferAvailable(uint32_t index, std::shared_ptr<AVBuffer> buffer)
43 {
44 if (!(buffer->flag_ & AVCODEC_BUFFER_FLAG_EOS)) {
45 int32_t aveQp {};
46 if (buffer->meta_->GetData(OHOS::Media::Tag::VIDEO_ENCODER_QP_AVERAGE, aveQp)) {
47 TLOGI("buffer->pts_[%" PRId64 "], qp[%d]", buffer->pts_, aveQp);
48 }
49 double mse {};
50 if (buffer->meta_->GetData(OHOS::Media::Tag::VIDEO_ENCODER_MSE, mse)) {
51 TLOGI("buffer->pts_[%" PRId64 "], mse[%f]", buffer->pts_, mse);
52 }
53 bool isLTR {};
54 if (buffer->meta_->GetData(OHOS::Media::Tag::VIDEO_PER_FRAME_IS_LTR, isLTR)) {
55 TLOGI("buffer->pts_[%" PRId64 "], isLTR[%d]", buffer->pts_, isLTR);
56 }
57 int32_t poc {};
58 if (buffer->meta_->GetData(OHOS::Media::Tag::VIDEO_PER_FRAME_POC, poc)) {
59 TLOGI("buffer->pts_[%" PRId64 "], poc[%d]", buffer->pts_, poc);
60 }
61 int32_t frameLayer {};
62 if (buffer->meta_->GetData(OHOS::Media::Tag::VIDEO_ENCODER_FRAME_TEMPORAL_ID, frameLayer)) {
63 TLOGI("buffer->pts_[%" PRId64 "], frameLayer[%d]", buffer->pts_, frameLayer);
64 }
65 }
66 tester_->AfterGotOutput(OH_AVCodecBufferAttr {
67 .pts = buffer->pts_,
68 .size = buffer->memory_ ? buffer->memory_->GetSize() : 0,
69 .flags = buffer->flag_,
70 });
71 lock_guard<mutex> lk(tester_->outputMtx_);
72 tester_->outputList_.emplace_back(index, buffer);
73 tester_->outputCond_.notify_all();
74 }
75
Create()76 bool TesterCodecBase::Create()
77 {
78 string mime = GetCodecMime(opt_.protocol);
79 string name = GetCodecName(opt_.isEncoder, mime);
80 auto begin = std::chrono::steady_clock::now();
81 CreateHCodecByName(name, codec_);
82 if (codec_ == nullptr) {
83 TLOGE("Create failed");
84 return false;
85 }
86 Media::Meta meta{};
87 int32_t err = codec_->Init(meta);
88 if (err != AVCS_ERR_OK) {
89 TLOGE("Init failed");
90 return false;
91 }
92 CostRecorder::Instance().Update(begin, "Create");
93 return true;
94 }
95
SetCallback()96 bool TesterCodecBase::SetCallback()
97 {
98 shared_ptr<CallBack> cb = make_shared<CallBack>(this);
99 auto begin = std::chrono::steady_clock::now();
100 int32_t err = codec_->SetCallback(cb);
101 if (err != AVCS_ERR_OK) {
102 TLOGE("SetCallback failed");
103 return false;
104 }
105 CostRecorder::Instance().Update(begin, "SetCallback");
106 return true;
107 }
108
Start()109 bool TesterCodecBase::Start()
110 {
111 auto begin = std::chrono::steady_clock::now();
112 int32_t err = codec_->Start();
113 if (err != AVCS_ERR_OK) {
114 TLOGE("Start failed");
115 return false;
116 }
117 CostRecorder::Instance().Update(begin, "Start");
118 return true;
119 }
120
Stop()121 bool TesterCodecBase::Stop()
122 {
123 auto begin = std::chrono::steady_clock::now();
124 int32_t err = codec_->Stop();
125 if (err != AVCS_ERR_OK) {
126 TLOGE("Stop failed");
127 return false;
128 }
129 CostRecorder::Instance().Update(begin, "Stop");
130 return true;
131 }
132
Release()133 bool TesterCodecBase::Release()
134 {
135 auto begin = std::chrono::steady_clock::now();
136 int32_t err = codec_->Release();
137 if (err != AVCS_ERR_OK) {
138 TLOGE("Release failed");
139 return false;
140 }
141 CostRecorder::Instance().Update(begin, "Release");
142 return true;
143 }
144
Flush()145 bool TesterCodecBase::Flush()
146 {
147 auto begin = std::chrono::steady_clock::now();
148 int32_t err = codec_->Flush();
149 if (err != AVCS_ERR_OK) {
150 TLOGE("Flush failed");
151 return false;
152 }
153 CostRecorder::Instance().Update(begin, "Flush");
154 return true;
155 }
156
ClearAllBuffer()157 void TesterCodecBase::ClearAllBuffer()
158 {
159 {
160 lock_guard<mutex> lk(inputMtx_);
161 inputList_.clear();
162 }
163 {
164 lock_guard<mutex> lk(outputMtx_);
165 outputList_.clear();
166 }
167 }
168
EnableHighPerf(Format & fmt) const169 void TesterCodecBase::EnableHighPerf(Format& fmt) const
170 {
171 if (opt_.isHighPerfMode) {
172 fmt.PutIntValue("frame_rate_adaptive_mode", 1);
173 }
174 }
175
ConfigureEncoder()176 bool TesterCodecBase::ConfigureEncoder()
177 {
178 Format fmt;
179 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_WIDTH, opt_.dispW);
180 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_HEIGHT, opt_.dispH);
181 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_PIXEL_FORMAT, static_cast<int32_t>(opt_.pixFmt));
182 fmt.PutDoubleValue(MediaDescriptionKey::MD_KEY_FRAME_RATE, opt_.frameRate);
183 if (opt_.rangeFlag.has_value()) {
184 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_RANGE_FLAG, opt_.rangeFlag.value());
185 }
186 if (opt_.primary.has_value()) {
187 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_COLOR_PRIMARIES, opt_.primary.value());
188 }
189 if (opt_.transfer.has_value()) {
190 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_TRANSFER_CHARACTERISTICS, opt_.transfer.value());
191 }
192 if (opt_.matrix.has_value()) {
193 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_MATRIX_COEFFICIENTS, opt_.matrix.value());
194 }
195 if (opt_.iFrameInterval.has_value()) {
196 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_I_FRAME_INTERVAL, opt_.iFrameInterval.value());
197 }
198 if (opt_.profile.has_value()) {
199 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_PROFILE, opt_.profile.value());
200 }
201 if (opt_.rateMode.has_value()) {
202 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_VIDEO_ENCODE_BITRATE_MODE, opt_.rateMode.value());
203 }
204 if (opt_.bitRate.has_value()) {
205 fmt.PutLongValue(MediaDescriptionKey::MD_KEY_BITRATE, opt_.bitRate.value());
206 }
207 if (opt_.quality.has_value()) {
208 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_QUALITY, opt_.quality.value());
209 }
210 if (opt_.layerCnt.has_value()) {
211 fmt.PutIntValue(OHOS::Media::Tag::VIDEO_ENCODER_ENABLE_TEMPORAL_SCALABILITY, true);
212 int32_t temporalGopSize = 0;
213 switch (opt_.layerCnt.value()) {
214 case 2: // 2: temporal layerCnt
215 temporalGopSize = 2; // 2: temporalGopSize
216 break;
217 case 3: // 3: temporal layerCnt
218 temporalGopSize = 4; // 4: temporalGopSize
219 break;
220 default:
221 break;
222 }
223 fmt.PutIntValue(OHOS::Media::Tag::VIDEO_ENCODER_TEMPORAL_GOP_SIZE, temporalGopSize);
224 fmt.PutIntValue(OHOS::Media::Tag::VIDEO_ENCODER_TEMPORAL_GOP_REFERENCE_MODE, 2); // 2: gop mode
225 }
226 EnableHighPerf(fmt);
227 if (opt_.qpRange.has_value()) {
228 fmt.PutIntValue(OHOS::Media::Tag::VIDEO_ENCODER_QP_MIN, opt_.qpRange->qpMin);
229 fmt.PutIntValue(OHOS::Media::Tag::VIDEO_ENCODER_QP_MAX, opt_.qpRange->qpMax);
230 }
231 if (opt_.repeatAfter.has_value()) {
232 fmt.PutIntValue(OHOS::Media::Tag::VIDEO_ENCODER_REPEAT_PREVIOUS_FRAME_AFTER, opt_.repeatAfter.value());
233 }
234 if (opt_.repeatMaxCnt.has_value()) {
235 fmt.PutIntValue(OHOS::Media::Tag::VIDEO_ENCODER_REPEAT_PREVIOUS_MAX_COUNT, opt_.repeatMaxCnt.value());
236 }
237 if (!opt_.isBufferMode && !opt_.perFrameParamsMap.empty()) {
238 fmt.PutIntValue(OHOS::Media::Tag::VIDEO_ENCODER_ENABLE_SURFACE_INPUT_CALLBACK, 1);
239 opt_.enableInputCb = true;
240 }
241 if (opt_.ltrFrameCount > 0) {
242 fmt.PutIntValue(OHOS::Media::Tag::VIDEO_ENCODER_LTR_FRAME_COUNT, opt_.ltrFrameCount);
243 }
244 if (opt_.paramsFeedback == 1) {
245 fmt.PutIntValue(OHOS::Media::Tag::VIDEO_ENCODER_ENABLE_PARAMS_FEEDBACK, opt_.paramsFeedback);
246 }
247 auto begin = std::chrono::steady_clock::now();
248 int32_t err = codec_->Configure(fmt);
249 if (err != AVCS_ERR_OK) {
250 TLOGE("Configure failed");
251 return false;
252 }
253 CostRecorder::Instance().Update(begin, "Configure");
254
255 if (opt_.waterMark.isSet) {
256 shared_ptr<AVBuffer> buffer = CreateWaterMarkBuffer();
257 err = codec_->SetCustomBuffer(buffer);
258 if (err != AVCS_ERR_OK) {
259 TLOGE("SetCustomBuffer failed");
260 return false;
261 }
262 }
263 return true;
264 }
265
SetEncoderParameter(const SetParameterParams & param)266 bool TesterCodecBase::SetEncoderParameter(const SetParameterParams& param)
267 {
268 Format fmt;
269 if (param.bitRate.has_value()) {
270 fmt.PutLongValue(MediaDescriptionKey::MD_KEY_BITRATE, param.bitRate.value());
271 }
272 if (param.frameRate.has_value()) {
273 fmt.PutDoubleValue(MediaDescriptionKey::MD_KEY_FRAME_RATE, param.frameRate.value());
274 }
275 if (param.qpRange.has_value()) {
276 fmt.PutIntValue(OHOS::Media::Tag::VIDEO_ENCODER_QP_MIN, static_cast<int32_t>(param.qpRange->qpMin));
277 fmt.PutIntValue(OHOS::Media::Tag::VIDEO_ENCODER_QP_MAX, static_cast<int32_t>(param.qpRange->qpMax));
278 }
279 if (opt_.scaleMode.has_value()) {
280 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_SCALE_TYPE, static_cast<int32_t>(opt_.scaleMode.value()));
281 }
282 int32_t err = codec_->SetParameter(fmt);
283 if (err != AVCS_ERR_OK) {
284 TLOGE("SetParameter failed");
285 return false;
286 }
287 return true;
288 }
289
SetEncoderPerFrameParam(BufInfo & buf,const PerFrameParams & param)290 bool TesterCodecBase::SetEncoderPerFrameParam(BufInfo& buf, const PerFrameParams& param)
291 {
292 if (buf.avbuf == nullptr) {
293 return false;
294 }
295 shared_ptr<Media::Meta> meta = buf.avbuf->meta_;
296 if (meta == nullptr) {
297 return false;
298 }
299 if (param.requestIdr.has_value()) {
300 meta->SetData(OHOS::Media::Tag::VIDEO_REQUEST_I_FRAME, param.requestIdr.value());
301 }
302 if (param.qpRange.has_value()) {
303 meta->SetData(OHOS::Media::Tag::VIDEO_ENCODER_QP_MIN, static_cast<int32_t>(param.qpRange->qpMin));
304 meta->SetData(OHOS::Media::Tag::VIDEO_ENCODER_QP_MAX, static_cast<int32_t>(param.qpRange->qpMax));
305 }
306 if (param.ltrParam.has_value()) {
307 meta->SetData(OHOS::Media::Tag::VIDEO_ENCODER_PER_FRAME_MARK_LTR,
308 static_cast<bool>(param.ltrParam->markAsLTR));
309 if (param.ltrParam->useLTR > 0) {
310 meta->SetData(OHOS::Media::Tag::VIDEO_ENCODER_PER_FRAME_USE_LTR,
311 static_cast<int32_t>(param.ltrParam->useLTRPoc));
312 }
313 }
314 if (param.discard.has_value()) {
315 meta->SetData(OHOS::Media::Tag::VIDEO_ENCODER_PER_FRAME_DISCARD, param.discard.value());
316 }
317 if (param.ebrParam.has_value()) {
318 meta->SetData(OHOS::Media::Tag::VIDEO_ENCODER_QP_MIN, static_cast<int32_t>(param.ebrParam->minQp));
319 meta->SetData(OHOS::Media::Tag::VIDEO_ENCODER_QP_MAX, static_cast<int32_t>(param.ebrParam->maxQp));
320 meta->SetData(OHOS::Media::Tag::VIDEO_ENCODER_QP_START, static_cast<int32_t>(param.ebrParam->startQp));
321 meta->SetData(OHOS::Media::Tag::VIDEO_PER_FRAME_IS_SKIP, static_cast<bool>(param.ebrParam->isSkip));
322 }
323 return true;
324 }
325
CreateInputSurface()326 sptr<Surface> TesterCodecBase::CreateInputSurface()
327 {
328 auto begin = std::chrono::steady_clock::now();
329 sptr<Surface> ret = codec_->CreateInputSurface();
330 if (ret == nullptr) {
331 TLOGE("CreateInputSurface failed");
332 return nullptr;
333 }
334 CostRecorder::Instance().Update(begin, "CreateInputSurface");
335 return ret;
336 }
337
NotifyEos()338 bool TesterCodecBase::NotifyEos()
339 {
340 auto begin = std::chrono::steady_clock::now();
341 int32_t err = codec_->NotifyEos();
342 if (err != AVCS_ERR_OK) {
343 TLOGE("NotifyEos failed");
344 return false;
345 }
346 CostRecorder::Instance().Update(begin, "NotifyEos");
347 return true;
348 }
349
RequestIDR()350 bool TesterCodecBase::RequestIDR()
351 {
352 auto begin = std::chrono::steady_clock::now();
353 int32_t err = codec_->SignalRequestIDRFrame();
354 if (err != AVCS_ERR_OK) {
355 TLOGE("RequestIDR failed");
356 return false;
357 }
358 CostRecorder::Instance().Update(begin, "SignalRequestIDRFrame");
359 return true;
360 }
361
GetInputFormat()362 bool TesterCodecBase::GetInputFormat()
363 {
364 auto begin = std::chrono::steady_clock::now();
365 int32_t err = codec_->GetInputFormat(inputFmt_);
366 if (err != AVCS_ERR_OK) {
367 TLOGE("GetInputFormat failed");
368 return false;
369 }
370 CostRecorder::Instance().Update(begin, "GetInputFormat");
371 return true;
372 }
373
GetOutputFormat()374 bool TesterCodecBase::GetOutputFormat()
375 {
376 Format fmt;
377 auto begin = std::chrono::steady_clock::now();
378 int32_t err = codec_->GetOutputFormat(fmt);
379 if (err != AVCS_ERR_OK) {
380 TLOGE("GetOutputFormat failed");
381 return false;
382 }
383 CostRecorder::Instance().Update(begin, "GetOutputFormat");
384 return true;
385 }
386
GetInputStride()387 optional<uint32_t> TesterCodecBase::GetInputStride()
388 {
389 int32_t stride = 0;
390 if (inputFmt_.GetIntValue("stride", stride)) {
391 return stride;
392 } else {
393 return nullopt;
394 }
395 }
396
WaitForInput(BufInfo & buf)397 bool TesterCodecBase::WaitForInput(BufInfo& buf)
398 {
399 {
400 unique_lock<mutex> lk(inputMtx_);
401 if (opt_.timeout == -1) {
402 inputCond_.wait(lk, [this] {
403 return !inputList_.empty();
404 });
405 } else {
406 bool ret = inputCond_.wait_for(lk, chrono::milliseconds(opt_.timeout), [this] {
407 return !inputList_.empty();
408 });
409 if (!ret) {
410 TLOGE("time out");
411 return false;
412 }
413 }
414 std::tie(buf.idx, buf.avbuf) = inputList_.front();
415 inputList_.pop_front();
416 }
417 if (buf.avbuf == nullptr) {
418 TLOGE("null avbuffer");
419 return false;
420 }
421 if (opt_.enableInputCb) {
422 return true;
423 }
424 if (buf.avbuf->memory_ == nullptr) {
425 TLOGE("null memory in avbuffer");
426 return false;
427 }
428 buf.va = buf.avbuf->memory_->GetAddr();
429 buf.capacity = buf.avbuf->memory_->GetCapacity();
430 if (opt_.isEncoder && opt_.isBufferMode) {
431 sptr<SurfaceBuffer> surfaceBuffer = buf.avbuf->memory_->GetSurfaceBuffer();
432 if (!SurfaceBufferToBufferInfo(buf, surfaceBuffer)) {
433 return false;
434 }
435 }
436 return true;
437 }
438
ReturnInput(const BufInfo & buf)439 bool TesterCodecBase::ReturnInput(const BufInfo& buf)
440 {
441 if (!opt_.enableInputCb) {
442 buf.avbuf->pts_ = buf.attr.pts;
443 buf.avbuf->flag_ = buf.attr.flags;
444 buf.avbuf->memory_->SetOffset(buf.attr.offset);
445 buf.avbuf->memory_->SetSize(buf.attr.size);
446 }
447
448 auto begin = std::chrono::steady_clock::now();
449 int32_t err = codec_->QueueInputBuffer(buf.idx);
450 if (err != AVCS_ERR_OK) {
451 TLOGE("QueueInputBuffer failed");
452 return false;
453 }
454 CostRecorder::Instance().Update(begin, "QueueInputBuffer");
455 return true;
456 }
457
WaitForOutput(BufInfo & buf)458 bool TesterCodecBase::WaitForOutput(BufInfo& buf)
459 {
460 {
461 unique_lock<mutex> lk(outputMtx_);
462 if (opt_.timeout == -1) {
463 outputCond_.wait(lk, [this] {
464 return !outputList_.empty();
465 });
466 } else {
467 bool waitRes = outputCond_.wait_for(lk, chrono::milliseconds(opt_.timeout), [this] {
468 return !outputList_.empty();
469 });
470 if (!waitRes) {
471 TLOGE("time out");
472 return false;
473 }
474 }
475 std::tie(buf.idx, buf.avbuf) = outputList_.front();
476 outputList_.pop_front();
477 }
478 if (buf.avbuf == nullptr) {
479 TLOGE("null avbuffer");
480 return false;
481 }
482 if (buf.avbuf->flag_ & AVCODEC_BUFFER_FLAG_EOS) {
483 TLOGI("output eos, quit loop");
484 return false;
485 }
486 buf.attr.pts = buf.avbuf->pts_;
487 if (buf.avbuf->memory_) {
488 buf.va = buf.avbuf->memory_->GetAddr();
489 buf.capacity = static_cast<size_t>(buf.avbuf->memory_->GetCapacity());
490 buf.attr.size = buf.avbuf->memory_->GetSize();
491 }
492 return true;
493 }
494
ReturnOutput(uint32_t idx)495 bool TesterCodecBase::ReturnOutput(uint32_t idx)
496 {
497 int32_t ret;
498 string apiName;
499 auto begin = std::chrono::steady_clock::now();
500 if (opt_.isEncoder || opt_.isBufferMode) {
501 ret = codec_->ReleaseOutputBuffer(idx);
502 apiName = "ReleaseOutputBuffer";
503 } else {
504 ret = codec_->RenderOutputBuffer(idx);
505 apiName = "RenderOutputBuffer";
506 }
507 if (ret != AVCS_ERR_OK) {
508 TLOGE("%s failed", apiName.c_str());
509 return false;
510 }
511 CostRecorder::Instance().Update(begin, apiName);
512 return true;
513 }
514
SetOutputSurface(sptr<Surface> & surface)515 bool TesterCodecBase::SetOutputSurface(sptr<Surface>& surface)
516 {
517 auto begin = std::chrono::steady_clock::now();
518 int32_t err = codec_->SetOutputSurface(surface);
519 if (err != AVCS_ERR_OK) {
520 TLOGE("SetOutputSurface failed");
521 return false;
522 }
523 CostRecorder::Instance().Update(begin, "SetOutputSurface");
524 return true;
525 }
526
ConfigureDecoder()527 bool TesterCodecBase::ConfigureDecoder()
528 {
529 Format fmt;
530 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_WIDTH, opt_.dispW);
531 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_HEIGHT, opt_.dispH);
532 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_PIXEL_FORMAT, static_cast<int32_t>(opt_.pixFmt));
533 fmt.PutDoubleValue(MediaDescriptionKey::MD_KEY_FRAME_RATE, opt_.frameRate);
534 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_ROTATION_ANGLE, opt_.rotation);
535 EnableHighPerf(fmt);
536 if (opt_.scaleMode.has_value()) {
537 fmt.PutIntValue(MediaDescriptionKey::MD_KEY_SCALE_TYPE, static_cast<int32_t>(opt_.scaleMode.value()));
538 }
539 if (opt_.isVrrEnable.has_value()) {
540 fmt.PutIntValue(OHOS::Media::Tag::VIDEO_DECODER_OUTPUT_ENABLE_VRR, opt_.isVrrEnable.value());
541 }
542 auto begin = std::chrono::steady_clock::now();
543 int32_t err = codec_->Configure(fmt);
544 if (err != AVCS_ERR_OK) {
545 TLOGE("Configure failed");
546 return false;
547 }
548 CostRecorder::Instance().Update(begin, "Configure");
549 return true;
550 }
551 }