OMXFocus.cpp revision 85c859b69b3c003b8db810371e24fe41599fc7de
1/* 2 * Copyright (C) Texas Instruments - http://www.ti.com/ 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18/** 19* @file OMXFocus.cpp 20* 21* This file contains functionality for handling focus configurations. 22* 23*/ 24 25#undef LOG_TAG 26 27#define LOG_TAG "CameraHAL" 28 29#include "CameraHal.h" 30#include "OMXCameraAdapter.h" 31#include "ErrorUtils.h" 32 33#define TOUCH_FOCUS_RANGE 0xFF 34#define AF_CALLBACK_TIMEOUT 10000000 //10 seconds timeout 35 36namespace android { 37 38status_t OMXCameraAdapter::setParametersFocus(const CameraParameters ¶ms, 39 BaseCameraAdapter::AdapterState state) 40{ 41 status_t ret = NO_ERROR; 42 const char *str = NULL; 43 44 LOG_FUNCTION_NAME; 45 46 str = params.get(CameraParameters::KEY_FOCUS_AREAS); 47 mFocusAreas.clear(); 48 if ( NULL != str ) { 49 ret = CameraArea::parseFocusArea(str, strlen(str), mFocusAreas); 50 } 51 52 if ( NO_ERROR == ret ) { 53 if ( MAX_FOCUS_AREAS < mFocusAreas.size() ) { 54 CAMHAL_LOGEB("Focus areas supported %d, focus areas set %d", 55 MAX_FOCUS_AREAS, 56 mFocusAreas.size()); 57 ret = -EINVAL; 58 } 59 } 60 61 LOG_FUNCTION_NAME; 62 63 return ret; 64} 65 66status_t OMXCameraAdapter::doAutoFocus() 67{ 68 status_t ret = NO_ERROR; 69 OMX_ERRORTYPE eError = OMX_ErrorNone; 70 OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE focusControl; 71 OMX_PARAM_FOCUSSTATUSTYPE focusStatus; 72 73 LOG_FUNCTION_NAME; 74 75 if ( OMX_StateExecuting != mComponentState ) 76 { 77 CAMHAL_LOGEA("OMX component not in executing state"); 78 returnFocusStatus(false); 79 return NO_INIT; 80 } 81 82 if ( 0 != mDoAFSem.Count() ) 83 { 84 CAMHAL_LOGEB("Error mDoAFSem semaphore count %d", mDoAFSem.Count()); 85 return NO_INIT; 86 } 87 88 // If the app calls autoFocus, the camera will stop sending face callbacks. 89 pauseFaceDetection(true); 90 91 OMX_INIT_STRUCT_PTR (&focusControl, OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE); 92 focusControl.eFocusControl = ( OMX_IMAGE_FOCUSCONTROLTYPE ) mParameters3A.Focus; 93 94 //In case we have CAF running we should first check the AF status. 95 //If it has managed to lock, then do as usual and return status 96 //immediately. If lock is not available, then switch temporarily 97 //to 'autolock' and do normal AF. 98 if ( mParameters3A.Focus == OMX_IMAGE_FocusControlAuto ) { 99//FIXME: The CAF seems to return focus failure all the time. 100// Probably this is tuning related, disable this until the 101// MMS IQ team fixes it 102#if 0 103 ret = checkFocus(&focusStatus); 104#else 105 ret = NO_ERROR; 106 focusStatus.eFocusStatus = OMX_FocusStatusReached; 107#endif 108 if ( NO_ERROR != ret ) { 109 CAMHAL_LOGEB("Focus status check failed 0x%x!", ret); 110 return ret; 111 } else { 112 CAMHAL_LOGDB("Focus status check 0x%x!", focusStatus.eFocusStatus); 113 } 114 115 if ( OMX_FocusStatusReached != focusStatus.eFocusStatus ) { 116 focusControl.eFocusControl = OMX_IMAGE_FocusControlAutoLock; 117 } 118 } 119 120 if ( ( focusControl.eFocusControl != OMX_IMAGE_FocusControlAuto ) && 121 ( focusControl.eFocusControl != ( OMX_IMAGE_FOCUSCONTROLTYPE ) 122 OMX_IMAGE_FocusControlAutoInfinity ) ) { 123 124 ret = RegisterForEvent(mCameraAdapterParameters.mHandleComp, 125 (OMX_EVENTTYPE) OMX_EventIndexSettingChanged, 126 OMX_ALL, 127 OMX_IndexConfigCommonFocusStatus, 128 mDoAFSem); 129 130 if ( NO_ERROR == ret ) { 131 ret = setFocusCallback(true); 132 } 133 134 } 135 136 eError = OMX_SetConfig(mCameraAdapterParameters.mHandleComp, 137 OMX_IndexConfigFocusControl, 138 &focusControl); 139 140 if ( OMX_ErrorNone != eError ) { 141 CAMHAL_LOGEB("Error while starting focus 0x%x", eError); 142 return INVALID_OPERATION; 143 } else { 144 CAMHAL_LOGDA("Autofocus started successfully"); 145 } 146 147 if ( ( focusControl.eFocusControl != OMX_IMAGE_FocusControlAuto ) && 148 ( focusControl.eFocusControl != ( OMX_IMAGE_FOCUSCONTROLTYPE ) 149 OMX_IMAGE_FocusControlAutoInfinity ) ) { 150 ret = mDoAFSem.WaitTimeout(AF_CALLBACK_TIMEOUT); 151 //Disable auto focus callback from Ducati 152 setFocusCallback(false); 153 //Signal a dummy AF event so that in case the callback from ducati 154 //does come then it doesnt crash after 155 //exiting this function since eventSem will go out of scope. 156 if(ret != NO_ERROR) { 157 CAMHAL_LOGEA("Autofocus callback timeout expired"); 158 SignalEvent(mCameraAdapterParameters.mHandleComp, 159 (OMX_EVENTTYPE) OMX_EventIndexSettingChanged, 160 OMX_ALL, 161 OMX_IndexConfigCommonFocusStatus, 162 NULL ); 163 returnFocusStatus(true); 164 } else { 165 CAMHAL_LOGDA("Autofocus callback received"); 166 ret = returnFocusStatus(false); 167 } 168 169 } else { 170 if ( NO_ERROR == ret ) { 171 ret = returnFocusStatus(false); 172 } 173 } 174 175 //Restore CAF if needed 176 if ( ( mParameters3A.Focus == OMX_IMAGE_FocusControlAuto ) && 177 ( focusControl.eFocusControl == OMX_IMAGE_FocusControlAutoLock ) ) { 178 mPending3Asettings |= SetFocus; 179 } 180 181 LOG_FUNCTION_NAME_EXIT; 182 183 return ret; 184} 185 186status_t OMXCameraAdapter::stopAutoFocus() 187{ 188 status_t ret = NO_ERROR; 189 OMX_ERRORTYPE eError = OMX_ErrorNone; 190 OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE focusControl; 191 192 LOG_FUNCTION_NAME; 193 194 if ( OMX_StateExecuting != mComponentState ) 195 { 196 CAMHAL_LOGEA("OMX component not in executing state"); 197 return NO_INIT; 198 } 199 200 if ( mParameters3A.Focus == OMX_IMAGE_FocusControlAutoInfinity ) { 201 // No need to stop focus if we are in infinity mode. Nothing to stop. 202 return NO_ERROR; 203 } 204 205 if ( NO_ERROR == ret ) 206 { 207 //Disable the callback first 208 ret = setFocusCallback(false); 209 } 210 211 if ( NO_ERROR == ret ) 212 { 213 OMX_INIT_STRUCT_PTR (&focusControl, OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE); 214 focusControl.eFocusControl = OMX_IMAGE_FocusControlOff; 215 216 eError = OMX_SetConfig(mCameraAdapterParameters.mHandleComp, 217 OMX_IndexConfigFocusControl, 218 &focusControl); 219 if ( OMX_ErrorNone != eError ) 220 { 221 CAMHAL_LOGEB("Error while stopping focus 0x%x", eError); 222 return ErrorUtils::omxToAndroidError(eError); 223 } 224 } 225 226 //Query current focus distance after AF is complete 227 updateFocusDistances(mParameters); 228 229 LOG_FUNCTION_NAME_EXIT; 230 231 return ret; 232} 233 234status_t OMXCameraAdapter::getFocusMode(OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE &focusMode) 235{; 236 OMX_ERRORTYPE eError = OMX_ErrorNone; 237 238 LOG_FUNCTION_NAME; 239 240 if ( OMX_StateInvalid == mComponentState ) { 241 CAMHAL_LOGEA("OMX component is in invalid state"); 242 return NO_INIT; 243 } 244 245 OMX_INIT_STRUCT_PTR (&focusMode, OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE); 246 focusMode.nPortIndex = mCameraAdapterParameters.mPrevPortIndex; 247 248 eError = OMX_GetConfig(mCameraAdapterParameters.mHandleComp, 249 OMX_IndexConfigFocusControl, 250 &focusMode); 251 252 if ( OMX_ErrorNone != eError ) { 253 CAMHAL_LOGEB("Error while retrieving focus mode 0x%x", eError); 254 } 255 256 LOG_FUNCTION_NAME_EXIT; 257 258 return ErrorUtils::omxToAndroidError(eError); 259} 260 261status_t OMXCameraAdapter::cancelAutoFocus() 262{ 263 status_t ret = NO_ERROR; 264 OMX_ERRORTYPE eError = OMX_ErrorNone; 265 OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE focusMode; 266 267 LOG_FUNCTION_NAME; 268 // Unlock 3A locks since they were locked by AF 269 if( set3ALock(OMX_FALSE) != NO_ERROR) { 270 CAMHAL_LOGEA("Error Unlocking 3A locks"); 271 } 272 else{ 273 CAMHAL_LOGDA("AE/AWB unlocked successfully"); 274 } 275 276 ret = getFocusMode(focusMode); 277 if ( NO_ERROR != ret ) { 278 return ret; 279 } 280 281 //Stop the AF only for modes other than CAF or Inifinity 282 if ( ( focusMode.eFocusControl != OMX_IMAGE_FocusControlAuto ) && 283 ( focusMode.eFocusControl != ( OMX_IMAGE_FOCUSCONTROLTYPE ) 284 OMX_IMAGE_FocusControlAutoInfinity ) ) { 285 stopAutoFocus(); 286 //Signal a dummy AF event so that in case the callback from ducati 287 //does come then it doesnt crash after 288 //exiting this function since eventSem will go out of scope. 289 ret |= SignalEvent(mCameraAdapterParameters.mHandleComp, 290 (OMX_EVENTTYPE) OMX_EventIndexSettingChanged, 291 OMX_ALL, 292 OMX_IndexConfigCommonFocusStatus, 293 NULL ); 294 } 295 296 // If the apps call #cancelAutoFocus()}, the face callbacks will also resume. 297 pauseFaceDetection(false); 298 299 LOG_FUNCTION_NAME_EXIT; 300 301 return ret; 302 303} 304 305status_t OMXCameraAdapter::setFocusCallback(bool enabled) 306{ 307 status_t ret = NO_ERROR; 308 OMX_ERRORTYPE eError = OMX_ErrorNone; 309 OMX_CONFIG_CALLBACKREQUESTTYPE focusRequstCallback; 310 311 LOG_FUNCTION_NAME; 312 313 if ( OMX_StateExecuting != mComponentState ) 314 { 315 CAMHAL_LOGEA("OMX component not in executing state"); 316 ret = -1; 317 } 318 319 if ( NO_ERROR == ret ) 320 { 321 322 OMX_INIT_STRUCT_PTR (&focusRequstCallback, OMX_CONFIG_CALLBACKREQUESTTYPE); 323 focusRequstCallback.nPortIndex = OMX_ALL; 324 focusRequstCallback.nIndex = OMX_IndexConfigCommonFocusStatus; 325 326 if ( enabled ) 327 { 328 focusRequstCallback.bEnable = OMX_TRUE; 329 } 330 else 331 { 332 focusRequstCallback.bEnable = OMX_FALSE; 333 } 334 335 eError = OMX_SetConfig(mCameraAdapterParameters.mHandleComp, 336 (OMX_INDEXTYPE) OMX_IndexConfigCallbackRequest, 337 &focusRequstCallback); 338 if ( OMX_ErrorNone != eError ) 339 { 340 CAMHAL_LOGEB("Error registering focus callback 0x%x", eError); 341 ret = -1; 342 } 343 else 344 { 345 CAMHAL_LOGDB("Autofocus callback for index 0x%x registered successfully", 346 OMX_IndexConfigCommonFocusStatus); 347 } 348 } 349 350 LOG_FUNCTION_NAME_EXIT; 351 352 return ret; 353} 354 355status_t OMXCameraAdapter::returnFocusStatus(bool timeoutReached) 356{ 357 status_t ret = NO_ERROR; 358 OMX_PARAM_FOCUSSTATUSTYPE eFocusStatus; 359 bool focusStatus = false; 360 BaseCameraAdapter::AdapterState state; 361 BaseCameraAdapter::getState(state); 362 363 LOG_FUNCTION_NAME; 364 365 OMX_INIT_STRUCT(eFocusStatus, OMX_PARAM_FOCUSSTATUSTYPE); 366 367 if( ( AF_ACTIVE & state ) != AF_ACTIVE ) 368 { 369 /// We don't send focus callback if focus was not started 370 return NO_ERROR; 371 } 372 373 if ( NO_ERROR == ret ) 374 { 375 376 if ( !timeoutReached ) 377 { 378 ret = checkFocus(&eFocusStatus); 379 380 if ( NO_ERROR != ret ) 381 { 382 CAMHAL_LOGEA("Focus status check failed!"); 383 } 384 } 385 } 386 387 if ( NO_ERROR == ret ) 388 { 389 390 if ( timeoutReached ) 391 { 392 focusStatus = false; 393 } 394 ///FIXME: The ducati seems to return focus as false always if continuous focus is enabled 395 ///So, return focus as locked always until this is fixed. 396 else if(mParameters3A.Focus == OMX_IMAGE_FocusControlAuto ) 397 { 398 focusStatus = true; 399 } 400 else 401 { 402 switch (eFocusStatus.eFocusStatus) 403 { 404 case OMX_FocusStatusReached: 405 { 406 focusStatus = true; 407 //Lock the AE and AWB here sinc the focus is locked 408 // Apply 3A locks after AF 409 if( set3ALock(OMX_TRUE) != NO_ERROR) { 410 CAMHAL_LOGEA("Error Applying 3A locks"); 411 } 412 else 413 { 414 CAMHAL_LOGDA("Focus locked. Applied focus locks successfully"); 415 } 416 break; 417 } 418 case OMX_FocusStatusOff: 419 case OMX_FocusStatusUnableToReach: 420 case OMX_FocusStatusRequest: 421 default: 422 { 423 focusStatus = false; 424 break; 425 } 426 } 427 428 stopAutoFocus(); 429 } 430 } 431 432 ret = BaseCameraAdapter::setState(CAMERA_CANCEL_AUTOFOCUS); 433 if ( NO_ERROR == ret ) 434 { 435 ret = BaseCameraAdapter::commitState(); 436 } 437 else 438 { 439 ret |= BaseCameraAdapter::rollbackState(); 440 } 441 442 if ( NO_ERROR == ret ) 443 { 444 notifyFocusSubscribers(focusStatus); 445 } 446 447 // After focus, face detection will resume sending face callbacks 448 pauseFaceDetection(false); 449 450 LOG_FUNCTION_NAME_EXIT; 451 452 return ret; 453} 454 455status_t OMXCameraAdapter::checkFocus(OMX_PARAM_FOCUSSTATUSTYPE *eFocusStatus) 456{ 457 status_t ret = NO_ERROR; 458 OMX_ERRORTYPE eError = OMX_ErrorNone; 459 460 LOG_FUNCTION_NAME; 461 462 if ( NULL == eFocusStatus ) 463 { 464 CAMHAL_LOGEA("Invalid focus status"); 465 ret = -EINVAL; 466 } 467 468 if ( OMX_StateExecuting != mComponentState ) 469 { 470 CAMHAL_LOGEA("OMX component not in executing state"); 471 ret = -EINVAL; 472 } 473 474 if ( NO_ERROR == ret ) 475 { 476 OMX_INIT_STRUCT_PTR (eFocusStatus, OMX_PARAM_FOCUSSTATUSTYPE); 477 478 eError = OMX_GetConfig(mCameraAdapterParameters.mHandleComp, 479 OMX_IndexConfigCommonFocusStatus, 480 eFocusStatus); 481 if ( OMX_ErrorNone != eError ) 482 { 483 CAMHAL_LOGEB("Error while retrieving focus status: 0x%x", eError); 484 ret = -1; 485 } 486 } 487 488 if ( NO_ERROR == ret ) 489 { 490 CAMHAL_LOGDB("Focus Status: %d", eFocusStatus->eFocusStatus); 491 } 492 493 LOG_FUNCTION_NAME_EXIT; 494 495 return ret; 496} 497 498status_t OMXCameraAdapter::updateFocusDistances(CameraParameters ¶ms) 499{ 500 OMX_U32 focusNear, focusOptimal, focusFar; 501 status_t ret = NO_ERROR; 502 503 LOG_FUNCTION_NAME; 504 505 ret = getFocusDistances(focusNear, focusOptimal, focusFar); 506 if ( NO_ERROR == ret) 507 { 508 ret = addFocusDistances(focusNear, focusOptimal, focusFar, params); 509 if ( NO_ERROR != ret ) 510 { 511 CAMHAL_LOGEB("Error in call to addFocusDistances() 0x%x", ret); 512 } 513 } 514 else 515 { 516 CAMHAL_LOGEB("Error in call to getFocusDistances() 0x%x", ret); 517 } 518 519 LOG_FUNCTION_NAME_EXIT; 520 521 return ret; 522} 523 524status_t OMXCameraAdapter::getFocusDistances(OMX_U32 &near,OMX_U32 &optimal, OMX_U32 &far) 525{ 526 status_t ret = NO_ERROR; 527 OMX_ERRORTYPE eError; 528 529 OMX_TI_CONFIG_FOCUSDISTANCETYPE focusDist; 530 531 LOG_FUNCTION_NAME; 532 533 if ( OMX_StateInvalid == mComponentState ) 534 { 535 CAMHAL_LOGEA("OMX component is in invalid state"); 536 ret = UNKNOWN_ERROR; 537 } 538 539 if ( NO_ERROR == ret ) 540 { 541 OMX_INIT_STRUCT_PTR(&focusDist, OMX_TI_CONFIG_FOCUSDISTANCETYPE); 542 focusDist.nPortIndex = mCameraAdapterParameters.mPrevPortIndex; 543 544 eError = OMX_GetConfig(mCameraAdapterParameters.mHandleComp, 545 ( OMX_INDEXTYPE ) OMX_TI_IndexConfigFocusDistance, 546 &focusDist); 547 if ( OMX_ErrorNone != eError ) 548 { 549 CAMHAL_LOGEB("Error while querying focus distances 0x%x", eError); 550 ret = UNKNOWN_ERROR; 551 } 552 553 } 554 555 if ( NO_ERROR == ret ) 556 { 557 near = focusDist.nFocusDistanceNear; 558 optimal = focusDist.nFocusDistanceOptimal; 559 far = focusDist.nFocusDistanceFar; 560 } 561 562 LOG_FUNCTION_NAME_EXIT; 563 564 return ret; 565} 566 567status_t OMXCameraAdapter::encodeFocusDistance(OMX_U32 dist, char *buffer, size_t length) 568{ 569 status_t ret = NO_ERROR; 570 uint32_t focusScale = 1000; 571 float distFinal; 572 573 LOG_FUNCTION_NAME; 574 575 if(mParameters3A.Focus == OMX_IMAGE_FocusControlAutoInfinity) 576 { 577 dist=0; 578 } 579 580 if ( NO_ERROR == ret ) 581 { 582 if ( 0 == dist ) 583 { 584 strncpy(buffer, CameraParameters::FOCUS_DISTANCE_INFINITY, ( length - 1 )); 585 } 586 else 587 { 588 distFinal = dist; 589 distFinal /= focusScale; 590 snprintf(buffer, ( length - 1 ) , "%5.3f", distFinal); 591 } 592 } 593 594 LOG_FUNCTION_NAME_EXIT; 595 596 return ret; 597} 598 599status_t OMXCameraAdapter::addFocusDistances(OMX_U32 &near, 600 OMX_U32 &optimal, 601 OMX_U32 &far, 602 CameraParameters& params) 603{ 604 status_t ret = NO_ERROR; 605 606 LOG_FUNCTION_NAME; 607 608 if ( NO_ERROR == ret ) 609 { 610 ret = encodeFocusDistance(near, mFocusDistNear, FOCUS_DIST_SIZE); 611 if ( NO_ERROR != ret ) 612 { 613 CAMHAL_LOGEB("Error encoding near focus distance 0x%x", ret); 614 } 615 } 616 617 if ( NO_ERROR == ret ) 618 { 619 ret = encodeFocusDistance(optimal, mFocusDistOptimal, FOCUS_DIST_SIZE); 620 if ( NO_ERROR != ret ) 621 { 622 CAMHAL_LOGEB("Error encoding near focus distance 0x%x", ret); 623 } 624 } 625 626 if ( NO_ERROR == ret ) 627 { 628 ret = encodeFocusDistance(far, mFocusDistFar, FOCUS_DIST_SIZE); 629 if ( NO_ERROR != ret ) 630 { 631 CAMHAL_LOGEB("Error encoding near focus distance 0x%x", ret); 632 } 633 } 634 635 if ( NO_ERROR == ret ) 636 { 637 snprintf(mFocusDistBuffer, ( FOCUS_DIST_BUFFER_SIZE - 1) ,"%s,%s,%s", mFocusDistNear, 638 mFocusDistOptimal, 639 mFocusDistFar); 640 641 params.set(CameraParameters::KEY_FOCUS_DISTANCES, mFocusDistBuffer); 642 } 643 644 LOG_FUNCTION_NAME_EXIT; 645 646 return ret; 647} 648 649status_t OMXCameraAdapter::setTouchFocus(size_t posX, 650 size_t posY, 651 size_t posWidth, 652 size_t posHeight, 653 size_t previewWidth, 654 size_t previewHeight) 655{ 656 status_t ret = NO_ERROR; 657 OMX_ERRORTYPE eError = OMX_ErrorNone; 658 OMX_CONFIG_EXTFOCUSREGIONTYPE touchControl; 659 660 LOG_FUNCTION_NAME; 661 662 if ( OMX_StateInvalid == mComponentState ) 663 { 664 CAMHAL_LOGEA("OMX component is in invalid state"); 665 ret = -1; 666 } 667 668 if ( NO_ERROR == ret ) 669 { 670 OMX_INIT_STRUCT_PTR (&touchControl, OMX_CONFIG_EXTFOCUSREGIONTYPE); 671 touchControl.nLeft = ( posX * TOUCH_FOCUS_RANGE ) / previewWidth; 672 touchControl.nTop = ( posY * TOUCH_FOCUS_RANGE ) / previewHeight; 673 touchControl.nWidth = ( posWidth * TOUCH_FOCUS_RANGE ) / previewWidth; 674 touchControl.nHeight = ( posHeight * TOUCH_FOCUS_RANGE ) / previewHeight; 675 676 eError = OMX_SetConfig(mCameraAdapterParameters.mHandleComp, 677 ( OMX_INDEXTYPE ) OMX_IndexConfigExtFocusRegion, 678 &touchControl); 679 if ( OMX_ErrorNone != eError ) 680 { 681 CAMHAL_LOGEB("Error while configuring touch focus 0x%x", eError); 682 ret = -1; 683 } 684 else 685 { 686 CAMHAL_LOGDB("Touch focus %d,%d %d,%d configured successfuly", 687 ( int ) touchControl.nLeft, 688 ( int ) touchControl.nTop, 689 ( int ) touchControl.nWidth, 690 ( int ) touchControl.nHeight); 691 } 692 } 693 694 LOG_FUNCTION_NAME_EXIT; 695 696 return ret; 697} 698 699}; 700