1# Photo Capture Practices (C/C++) 2<!--Kit: Camera Kit--> 3<!--Subsystem: Multimedia--> 4<!--Owner: @qano--> 5<!--SE: @leo_ysl--> 6<!--TSE: @xchaosioda--> 7 8Before developing a camera application, request permissions by following the instructions provided in [Requesting Camera Development Permissions](camera-preparation.md). 9 10This topic provides sample code that covers the complete photo capture process and the API calling sequence. For details about a single process (such as device input, session management, and photo capture), see the corresponding C/C++ development guide links provided in [Requesting Camera Development Permissions](camera-preparation.md). 11 12## Development Process 13 14After obtaining the output stream capabilities supported by the camera, create a photo stream. The development process is as follows: 15 16 17 18## Sample Code 19 201. Link the dynamic library in the CMake script. 21 ```txt 22 target_link_libraries(entry PUBLIC 23 libace_napi.z.so 24 libhilog_ndk.z.so 25 libnative_buffer.so 26 libohcamera.so 27 libohimage.so 28 libohfileuri.so 29 ) 30 ``` 312. Create the header file **ndk_camera.h**. 32 ```c++ 33 #include "ohcamera/camera.h" 34 #include "ohcamera/camera_input.h" 35 #include "ohcamera/capture_session.h" 36 #include "ohcamera/photo_output.h" 37 #include "ohcamera/preview_output.h" 38 #include "ohcamera/video_output.h" 39 #include "ohcamera/camera_manager.h" 40 41 class NDKCamera { 42 public: 43 ~NDKCamera(); 44 NDKCamera(char* previewId); 45 Camera_ErrorCode RegisterBufferCb(void* cb); 46 }; 47 ``` 48 493. Import the NDK APIs on the C++ side, and perform photo capture based on the surface ID passed in. 50 ```c++ 51 #include "hilog/log.h" 52 #include "ndk_camera.h" 53 54 void CaptureSessionOnFocusStateChange(Camera_CaptureSession* session, Camera_FocusState focusState) 55 { 56 OH_LOG_INFO(LOG_APP, "CaptureSessionOnFocusStateChange"); 57 } 58 59 void CaptureSessionOnError(Camera_CaptureSession* session, Camera_ErrorCode errorCode) 60 { 61 OH_LOG_INFO(LOG_APP, "CaptureSessionOnError = %{public}d", errorCode); 62 } 63 64 CaptureSession_Callbacks* GetCaptureSessionRegister(void) 65 { 66 static CaptureSession_Callbacks captureSessionCallbacks = { 67 .onFocusStateChange = CaptureSessionOnFocusStateChange, 68 .onError = CaptureSessionOnError 69 }; 70 return &captureSessionCallbacks; 71 } 72 73 void PreviewOutputOnFrameStart(Camera_PreviewOutput* previewOutput) 74 { 75 OH_LOG_INFO(LOG_APP, "PreviewOutputOnFrameStart"); 76 } 77 78 void PreviewOutputOnFrameEnd(Camera_PreviewOutput* previewOutput, int32_t frameCount) 79 { 80 OH_LOG_INFO(LOG_APP, "PreviewOutputOnFrameEnd = %{public}d", frameCount); 81 } 82 83 void PreviewOutputOnError(Camera_PreviewOutput* previewOutput, Camera_ErrorCode errorCode) 84 { 85 OH_LOG_INFO(LOG_APP, "PreviewOutputOnError = %{public}d", errorCode); 86 } 87 88 PreviewOutput_Callbacks* GetPreviewOutputListener(void) 89 { 90 static PreviewOutput_Callbacks previewOutputListener = { 91 .onFrameStart = PreviewOutputOnFrameStart, 92 .onFrameEnd = PreviewOutputOnFrameEnd, 93 .onError = PreviewOutputOnError 94 }; 95 return &previewOutputListener; 96 } 97 98 void OnCameraInputError(const Camera_Input* cameraInput, Camera_ErrorCode errorCode) 99 { 100 OH_LOG_INFO(LOG_APP, "OnCameraInput errorCode = %{public}d", errorCode); 101 } 102 103 CameraInput_Callbacks* GetCameraInputListener(void) 104 { 105 static CameraInput_Callbacks cameraInputCallbacks = { 106 .onError = OnCameraInputError 107 }; 108 return &cameraInputCallbacks; 109 } 110 111 void CameraManagerStatusCallback(Camera_Manager* cameraManager, Camera_StatusInfo* status) 112 { 113 OH_LOG_INFO(LOG_APP, "CameraManagerStatusCallback is called"); 114 } 115 116 CameraManager_Callbacks* GetCameraManagerListener() 117 { 118 static CameraManager_Callbacks cameraManagerListener = { 119 .onCameraStatus = CameraManagerStatusCallback 120 }; 121 return &cameraManagerListener; 122 } 123 124 static void* bufferCb = nullptr; 125 Camera_ErrorCode NDKCamera::RegisterBufferCb(void* cb) { 126 OH_LOG_INFO(LOG_APP, " RegisterBufferCb start"); 127 if (cb == nullptr) { 128 OH_LOG_INFO(LOG_APP, " RegisterBufferCb invalid error"); 129 return CAMERA_INVALID_ARGUMENT; 130 } 131 bufferCb = cb; 132 133 return CAMERA_OK; 134 } 135 void OnPhotoAvailable(Camera_PhotoOutput* photoOutput, OH_PhotoNative* photo) { 136 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable start!"); 137 OH_ImageNative* imageNative; 138 Camera_ErrorCode errCode = OH_PhotoNative_GetMainImage(photo, &imageNative); 139 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable errCode:%{public}d imageNative:%{public}p", errCode, imageNative); 140 // Read the size attribute of OH_ImageNative. 141 Image_Size size; 142 Image_ErrorCode imageErr = OH_ImageNative_GetImageSize(imageNative, &size); 143 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable imageErr:%{public}d width:%{public}d height:%{public}d", imageErr, 144 size.width, size.height); 145 // Read the number of elements in the component list of OH_ImageNative. 146 size_t componentTypeSize = 0; 147 imageErr = OH_ImageNative_GetComponentTypes(imageNative, nullptr, &componentTypeSize); 148 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable imageErr:%{public}d componentTypeSize:%{public}zu", imageErr, 149 componentTypeSize); 150 // Read the component list of OH_ImageNative. 151 uint32_t* components = new uint32_t[componentTypeSize]; 152 imageErr = OH_ImageNative_GetComponentTypes(imageNative, &components, &componentTypeSize); 153 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable OH_ImageNative_GetComponentTypes imageErr:%{public}d", imageErr); 154 // Read the buffer object corresponding to the first component of OH_ImageNative. 155 OH_NativeBuffer* nativeBuffer = nullptr; 156 imageErr = OH_ImageNative_GetByteBuffer(imageNative, components[0], &nativeBuffer); 157 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable OH_ImageNative_GetByteBuffer imageErr:%{public}d", imageErr); 158 // Read the size of the buffer corresponding to the first component of OH_ImageNative. 159 size_t nativeBufferSize = 0; 160 imageErr = OH_ImageNative_GetBufferSize(imageNative, components[0], &nativeBufferSize); 161 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable imageErr:%{public}d nativeBufferSize:%{public}zu", imageErr, 162 nativeBufferSize); 163 // Read the row stride corresponding to the first component of OH_ImageNative. 164 int32_t rowStride = 0; 165 imageErr = OH_ImageNative_GetRowStride(imageNative, components[0], &rowStride); 166 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable imageErr:%{public}d rowStride:%{public}d", imageErr, rowStride); 167 // Read the pixel stride corresponding to the first component of OH_ImageNative. 168 int32_t pixelStride = 0; 169 imageErr = OH_ImageNative_GetPixelStride(imageNative, components[0], &pixelStride); 170 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable imageErr:%{public}d pixelStride:%{public}d", imageErr, pixelStride); 171 // Map the ION memory to the process address space. 172 void* virAddr = nullptr; // Point to the virtual address of the mapped memory. After unmapping, the pointer is invalid. 173 int32_t ret = OH_NativeBuffer_Map(nativeBuffer, &virAddr); // After mapping, the start address of the memory is returned through the parameter virAddr. 174 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable OH_NativeBuffer_Map err:%{public}d", ret); 175 // Pass the processed buffer to the ArkTS side through the callback for image display or storage (using a security component). For details, see Photo Capture (C/C++). 176 auto cb = (void (*)(void *, size_t))(bufferCb); 177 cb(virAddr, nativeBufferSize); 178 // After the processing is complete, unmap and release the buffer. 179 ret = OH_NativeBuffer_Unmap(nativeBuffer); 180 if (ret != 0) { 181 OH_LOG_ERROR(LOG_APP, "OnPhotoAvailable OH_NativeBuffer_Unmap error:%{public}d", ret); 182 } 183 } 184 185 NDKCamera::NDKCamera(char* previewId) 186 { 187 Camera_Manager* cameraManager = nullptr; 188 Camera_Device* cameras = nullptr; 189 Camera_CaptureSession* captureSession = nullptr; 190 Camera_OutputCapability* cameraOutputCapability = nullptr; 191 const Camera_Profile* previewProfile = nullptr; 192 const Camera_Profile* photoProfile = nullptr; 193 Camera_PreviewOutput* previewOutput = nullptr; 194 Camera_PhotoOutput* photoOutput = nullptr; 195 Camera_Input* cameraInput = nullptr; 196 uint32_t size = 0; 197 uint32_t cameraDeviceIndex = 0; 198 char* previewSurfaceId = previewId; 199 // Create a CameraManager object. 200 Camera_ErrorCode ret = OH_Camera_GetCameraManager(&cameraManager); 201 if (cameraManager == nullptr || ret != CAMERA_OK) { 202 OH_LOG_ERROR(LOG_APP, "OH_Camera_GetCameraMananger failed."); 203 return; 204 } 205 // Listen for camera status changes. 206 ret = OH_CameraManager_RegisterCallback(cameraManager, GetCameraManagerListener()); 207 if (ret != CAMERA_OK) { 208 OH_LOG_ERROR(LOG_APP, "OH_CameraManager_RegisterCallback failed."); 209 } 210 211 // Obtain the camera list. 212 ret = OH_CameraManager_GetSupportedCameras(cameraManager, &cameras, &size); 213 if (cameras == nullptr || size <= 0 || ret != CAMERA_OK) { 214 OH_LOG_ERROR(LOG_APP, "OH_CameraManager_GetSupportedCameras failed."); 215 return; 216 } 217 218 if (size < cameraDeviceIndex + 1) { 219 OH_LOG_ERROR(LOG_APP, "cameraDeviceIndex is invalid."); 220 return; 221 } 222 223 // Create a camera input stream. 224 ret = OH_CameraManager_CreateCameraInput(cameraManager, &cameras[cameraDeviceIndex], &cameraInput); 225 if (cameraInput == nullptr || ret != CAMERA_OK) { 226 OH_LOG_ERROR(LOG_APP, "OH_CameraManager_CreateCameraInput failed."); 227 return; 228 } 229 230 // Listen for camera input errors. 231 ret = OH_CameraInput_RegisterCallback(cameraInput, GetCameraInputListener()); 232 if (ret != CAMERA_OK) { 233 OH_LOG_ERROR(LOG_APP, "OH_CameraInput_RegisterCallback failed."); 234 return; 235 } 236 237 // Open the camera. 238 ret = OH_CameraInput_Open(cameraInput); 239 if (ret != CAMERA_OK) { 240 OH_LOG_ERROR(LOG_APP, "OH_CameraInput_Open failed."); 241 return; 242 } 243 244 // Obtain the output stream capability supported by the camera. 245 ret = OH_CameraManager_GetSupportedCameraOutputCapability(cameraManager, &cameras[cameraDeviceIndex], 246 &cameraOutputCapability); 247 if (cameraOutputCapability == nullptr || ret != CAMERA_OK) { 248 OH_LOG_ERROR(LOG_APP, "OH_CameraManager_GetSupportedCameraOutputCapability failed."); 249 return; 250 } 251 252 if (cameraOutputCapability->previewProfiles == nullptr) { 253 OH_LOG_ERROR(LOG_APP, "previewProfiles == null"); 254 return; 255 } 256 // Select an appropriate preview resolution from previewProfiles of cameraOutputCapability. 257 previewProfile = cameraOutputCapability->previewProfiles[0]; 258 259 if (cameraOutputCapability->photoProfiles == nullptr) { 260 OH_LOG_ERROR(LOG_APP, "photoProfiles == null"); 261 return; 262 } 263 // Select an appropriate photo resolution from photoProfiles of cameraOutputCapability. 264 photoProfile = cameraOutputCapability->photoProfiles[0]; 265 266 // Create a preview output stream, with the previewSurfaceId parameter set to the ID of the surface provided by the XComponent. 267 ret = OH_CameraManager_CreatePreviewOutput(cameraManager, previewProfile, previewSurfaceId, &previewOutput); 268 if (previewProfile == nullptr || previewOutput == nullptr || ret != CAMERA_OK) { 269 OH_LOG_ERROR(LOG_APP, "OH_CameraManager_CreatePreviewOutput failed."); 270 return; 271 } 272 273 // Listen for preview output errors. 274 ret = OH_PreviewOutput_RegisterCallback(previewOutput, GetPreviewOutputListener()); 275 if (ret != CAMERA_OK) { 276 OH_LOG_ERROR(LOG_APP, "OH_PreviewOutput_RegisterCallback failed."); 277 } 278 279 // Create a photo output stream. 280 ret = OH_CameraManager_CreatePhotoOutputWithoutSurface(cameraManager, photoProfile, &photoOutput); 281 282 // Listen for the one-time photo capture callback. 283 ret = OH_PhotoOutput_RegisterPhotoAvailableCallback(photoOutput, OnPhotoAvailable); 284 285 // Create a session. 286 ret = OH_CameraManager_CreateCaptureSession(cameraManager, &captureSession); 287 if (captureSession == nullptr || ret != CAMERA_OK) { 288 OH_LOG_ERROR(LOG_APP, "OH_CameraManager_CreateCaptureSession failed."); 289 return; 290 } 291 292 // Listen for session errors. 293 ret = OH_CaptureSession_RegisterCallback(captureSession, GetCaptureSessionRegister()); 294 if (ret != CAMERA_OK) { 295 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_RegisterCallback failed."); 296 } 297 298 // Start configuration for the session. 299 ret = OH_CaptureSession_BeginConfig(captureSession); 300 if (ret != CAMERA_OK) { 301 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_BeginConfig failed."); 302 return; 303 } 304 305 // Add the camera input stream to the session. 306 ret = OH_CaptureSession_AddInput(captureSession, cameraInput); 307 if (ret != CAMERA_OK) { 308 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_AddInput failed."); 309 return; 310 } 311 312 // Add the preview output stream to the session. 313 ret = OH_CaptureSession_AddPreviewOutput(captureSession, previewOutput); 314 if (ret != CAMERA_OK) { 315 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_AddPreviewOutput failed."); 316 return; 317 } 318 319 // Add the photo output stream to the session. 320 ret = OH_CaptureSession_AddPhotoOutput(captureSession, photoOutput); 321 if (ret != CAMERA_OK) { 322 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_AddPhotoOutput failed."); 323 return; 324 } 325 326 // Commit the session configuration. 327 ret = OH_CaptureSession_CommitConfig(captureSession); 328 if (ret != CAMERA_OK) { 329 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_CommitConfig failed."); 330 return; 331 } 332 333 // Start the session. 334 ret = OH_CaptureSession_Start(captureSession); 335 if (ret != CAMERA_OK) { 336 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_Start failed."); 337 return; 338 } 339 340 // Check whether the camera has flash. 341 Camera_FlashMode flashMode = FLASH_MODE_AUTO; 342 bool hasFlash = false; 343 ret = OH_CaptureSession_HasFlash(captureSession, &hasFlash); 344 if (ret != CAMERA_OK) { 345 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_HasFlash failed."); 346 } 347 if (hasFlash) { 348 OH_LOG_INFO(LOG_APP, "hasFlash success"); 349 } else { 350 OH_LOG_ERROR(LOG_APP, "hasFlash fail"); 351 } 352 353 // Check whether a flash mode is supported. 354 bool isSupported = false; 355 ret = OH_CaptureSession_IsFlashModeSupported(captureSession, flashMode, &isSupported); 356 if (ret != CAMERA_OK) { 357 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_IsFlashModeSupported failed."); 358 } 359 if (isSupported) { 360 OH_LOG_INFO(LOG_APP, "isFlashModeSupported success"); 361 362 // Set the flash mode. 363 ret = OH_CaptureSession_SetFlashMode(captureSession, flashMode); 364 if (ret == CAMERA_OK) { 365 OH_LOG_INFO(LOG_APP, "OH_CaptureSession_SetFlashMode success."); 366 } else { 367 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_SetFlashMode failed. %{public}d ", ret); 368 } 369 370 // Obtain the flash mode in use. 371 ret = OH_CaptureSession_GetFlashMode(captureSession, &flashMode); 372 if (ret == CAMERA_OK) { 373 OH_LOG_INFO(LOG_APP, "OH_CaptureSession_GetFlashMode success. flashMode: %{public}d ", flashMode); 374 } else { 375 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_GetFlashMode failed. %d ", ret); 376 } 377 } else { 378 OH_LOG_ERROR(LOG_APP, "isFlashModeSupported fail"); 379 } 380 381 // Check whether the continuous auto focus is supported. 382 Camera_FocusMode focusMode = FOCUS_MODE_CONTINUOUS_AUTO; 383 bool isFocusModeSupported = false; 384 ret = OH_CaptureSession_IsFocusModeSupported(captureSession, focusMode, &isFocusModeSupported); 385 if (ret != CAMERA_OK) { 386 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_IsFocusModeSupported failed."); 387 } 388 if (isFocusModeSupported) { 389 OH_LOG_INFO(LOG_APP, "isFocusModeSupported success"); 390 ret = OH_CaptureSession_SetFocusMode(captureSession, focusMode); 391 if (ret != CAMERA_OK) { 392 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_SetFocusMode failed. %{public}d ", ret); 393 } 394 ret = OH_CaptureSession_GetFocusMode(captureSession, &focusMode); 395 if (ret == CAMERA_OK) { 396 OH_LOG_INFO(LOG_APP, "OH_CaptureSession_GetFocusMode success. focusMode%{public}d ", focusMode); 397 } else { 398 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_GetFocusMode failed. %d ", ret); 399 } 400 } else { 401 OH_LOG_ERROR(LOG_APP, "isFocusModeSupported fail"); 402 } 403 404 // Obtain the zoom ratio range supported by the camera. 405 float minZoom; 406 float maxZoom; 407 ret = OH_CaptureSession_GetZoomRatioRange(captureSession, &minZoom, &maxZoom); 408 if (ret != CAMERA_OK) { 409 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_GetZoomRatioRange failed."); 410 } else { 411 OH_LOG_INFO(LOG_APP, "OH_CaptureSession_GetZoomRatioRange success. minZoom: %{public}f, maxZoom:%{public}f", 412 minZoom, maxZoom); 413 } 414 415 // Set a zoom ratio. 416 ret = OH_CaptureSession_SetZoomRatio(captureSession, maxZoom); 417 if (ret == CAMERA_OK) { 418 OH_LOG_INFO(LOG_APP, "OH_CaptureSession_SetZoomRatio success."); 419 } else { 420 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_SetZoomRatio failed. %{public}d ", ret); 421 } 422 423 // Obtain the zoom ratio of the camera. 424 ret = OH_CaptureSession_GetZoomRatio(captureSession, &maxZoom); 425 if (ret == CAMERA_OK) { 426 OH_LOG_INFO(LOG_APP, "OH_CaptureSession_GetZoomRatio success. zoom: %{public}f ", maxZoom); 427 } else { 428 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_GetZoomRatio failed. %{public}d ", ret); 429 } 430 431 // Take photos without photo capture settings. 432 ret = OH_PhotoOutput_Capture(photoOutput); 433 if (ret == CAMERA_OK) { 434 OH_LOG_INFO(LOG_APP, "OH_PhotoOutput_Capture success "); 435 } else { 436 OH_LOG_ERROR(LOG_APP, "OH_PhotoOutput_Capture failed. %d ", ret); 437 } 438 439 // Stop the session. 440 ret = OH_CaptureSession_Stop(captureSession); 441 if (ret == CAMERA_OK) { 442 OH_LOG_INFO(LOG_APP, "OH_CaptureSession_Stop success "); 443 } else { 444 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_Stop failed. %d ", ret); 445 } 446 447 // Release the camera input stream. 448 ret = OH_CameraInput_Close(cameraInput); 449 if (ret == CAMERA_OK) { 450 OH_LOG_INFO(LOG_APP, "OH_CameraInput_Close success "); 451 } else { 452 OH_LOG_ERROR(LOG_APP, "OH_CameraInput_Close failed. %d ", ret); 453 } 454 455 // Release the preview output stream. 456 ret = OH_PreviewOutput_Release(previewOutput); 457 if (ret == CAMERA_OK) { 458 OH_LOG_INFO(LOG_APP, "OH_PreviewOutput_Release success "); 459 } else { 460 OH_LOG_ERROR(LOG_APP, "OH_PreviewOutput_Release failed. %d ", ret); 461 } 462 463 // Release the photo output stream. 464 ret = OH_PhotoOutput_Release(photoOutput); 465 if (ret == CAMERA_OK) { 466 OH_LOG_INFO(LOG_APP, "OH_PhotoOutput_Release success "); 467 } else { 468 OH_LOG_ERROR(LOG_APP, "OH_PhotoOutput_Release failed. %d ", ret); 469 } 470 471 // Release the session. 472 ret = OH_CaptureSession_Release(captureSession); 473 if (ret == CAMERA_OK) { 474 OH_LOG_INFO(LOG_APP, "OH_CaptureSession_Release success "); 475 } else { 476 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_Release failed. %d ", ret); 477 } 478 479 // Release the resources. 480 ret = OH_CameraManager_DeleteSupportedCameras(cameraManager, cameras, size); 481 if (ret != CAMERA_OK) { 482 OH_LOG_ERROR(LOG_APP, "Delete Cameras failed."); 483 } else { 484 OH_LOG_ERROR(LOG_APP, "OH_CameraManager_DeleteSupportedCameras. ok"); 485 } 486 ret = OH_CameraManager_DeleteSupportedCameraOutputCapability(cameraManager, cameraOutputCapability); 487 if (ret != CAMERA_OK) { 488 OH_LOG_ERROR(LOG_APP, "Delete Cameras failed."); 489 } else { 490 OH_LOG_ERROR(LOG_APP, "OH_CameraManager_DeleteSupportedCameraOutputCapability. ok"); 491 } 492 ret = OH_Camera_DeleteCameraManager(cameraManager); 493 if (ret != CAMERA_OK) { 494 OH_LOG_ERROR(LOG_APP, "Delete Cameras failed."); 495 } else { 496 OH_LOG_ERROR(LOG_APP, "OH_Camera_DeleteCameraManager. ok"); 497 } 498 } 499 ``` 500