# API Reference Source: https://facebetter.mintlify.app/android/api-reference Android API Reference ## Logging Related ### Log Levels Log level enumeration for controlling log output levels. ```java theme={null} public enum LogLevel { TRACE(0, "TRACE"), DEBUG(1, "DEBUG"), INFO(2, "INFO"), WARN(3, "WARN"), ERROR(4, "ERROR"), CRITICAL(5, "CRITICAL"); } ``` ### Log Configuration Class Log configuration class for configuring log output methods and levels. **Field Descriptions:** * `consoleEnabled`: Whether to enable console output * `fileEnabled`: Whether to enable file output * `level`: Log level * `fileName`: Log file path (only effective when `fileEnabled` is `true`) ```java theme={null} public static class LogConfig { public boolean consoleEnabled; public boolean fileEnabled; public LogLevel level; public String fileName; public LogConfig(); public LogConfig(boolean consoleEnabled, boolean fileEnabled, LogLevel level, String fileName); } ``` ## Engine Related ### Process Mode Image processing mode enumeration. * `IMAGE`: Image mode, suitable for single image processing * `VIDEO`: Video mode, suitable for video streams and live streaming scenarios, better performance ```java theme={null} public enum ProcessMode { IMAGE(0), // Image mode VIDEO(1); // Video mode } ``` ### Engine Configuration Engine configuration class for initializing the beauty engine. **Field Descriptions:** * `appId`: Application ID (optional, not required if `licenseJson` is provided) * `appKey`: Application key (optional, not required if `licenseJson` is provided) * `licenseJson`: License data JSON string (optional, if provided, takes priority and `appId` and `appKey` are not required) * `externalContext`: Whether to use external OpenGL context (default `false`) * `true`: Use the caller-provided GL context, SDK will not create/manage internal context * `false`: Use internal default context **Methods:** * `isValid()`: Validate if configuration is valid * If `licenseJson` is provided, use license data verification * Otherwise, require `appId` and `appKey` for automatic online verification **Verification Priority:** * If `licenseJson` is not empty, use license data verification (supports online response and offline license) * Otherwise, use `appId` and `appKey` for automatic online verification ```java theme={null} public static class EngineConfig { public String appId; public String appKey; public String licenseJson; // License data JSON string (optional) public boolean externalContext = false; // Whether to use external OpenGL context public EngineConfig(); public EngineConfig(String appId, String appKey); public boolean isValid(); } ``` ### Engine Interface Main beauty effect engine class providing entry point for beauty functionality. **Static Methods:** * `setLogConfig(LogConfig config)`: Set log configuration **Instance Methods:** #### Initialization and Release * `BeautyEffectEngine(Context context, EngineConfig config)`: Constructor, create engine instance * `void release()`: Release engine resources #### Parameter Settings * `int setBeautyParam(BasicParam param, float value)`: Set basic beauty parameters (range 0.0 - 1.0) * `int setBeautyParam(ReshapeParam param, float value)`: Set face reshape parameters (range 0.0 - 1.0) * `int setBeautyParam(MakeupParam param, float value)`: Set makeup parameters (range 0.0 - 1.0) * `int setLipstickStyle(LipstickStyle style)`: Set lipstick style (Rouge / Coral / Pink) * `int setBlushStyle(BlushStyle style)`: Set blush style (Classic / Peach / Rose) * `int setSkinOnlyBeauty(boolean enabled)`: Set whether beauty is applied only to skin regions * Parameter: `enabled` - `true` to enable skin-only beauty, `false` to apply to entire image * Return value: `0` indicates success, `-1` indicates engine not initialized * `int setVirtualBackground(VirtualBackgroundOptions options)`: Set virtual background * Parameter: `VirtualBackgroundOptions` object containing background mode and background image * Return value: `0` indicates success, `-1` indicates engine not initialized, `-2` indicates invalid parameters (options is null or invalid) #### Filters & Stickers Management * `int setFilter(String filterId)`: Set filter * Parameter: `filterId` unique filter identifier * `int setFilterIntensity(float intensity)`: Set filter intensity * Parameter: `intensity` intensity value (range 0.0 - 1.0) * `int setSticker(String stickerId)`: Set sticker * Parameter: `stickerId` unique sticker identifier, pass an empty string to clear the sticker * `int registerFilter(String filterId, String fbdFilePath)`: Register filter from file * `int registerFilter(String filterId, byte[] fbdData)`: Register filter from memory * `int registerSticker(String stickerId, String fbdFilePath)`: Register sticker from file * `int registerSticker(String stickerId, byte[] fbdData)`: Register sticker from memory * `int unregisterFilter(String filterId)`: Unload filter * `int unregisterAllFilters()`: Unload all filters * `int unregisterSticker(String stickerId)`: Unload sticker * `int unregisterAllStickers()`: Unload all stickers * `String[] getRegisteredFilters()`: Get list of registered filters * `String[] getRegisteredStickers()`: Get list of registered stickers #### Callbacks * `void setEngineEventCallback(OnEngineEventCallback callback)`: Set engine event callback to monitor license verification and engine initialization status * Callback provides: `int code` (event code), `String message` (event description) * Code 0 indicates success, non-zero indicates failure #### Image Processing * `ImageFrame processImage(ImageFrame inputFrame)`: Process image frame * Frame type is obtained from `inputFrame.type` field (`FrameType.IMAGE` or `FrameType.VIDEO`) * The processed image frame will maintain the same frame type **Return Value Descriptions:** * Methods return `int` type: * `0` indicates success * `-1` indicates engine not initialized * `-2` indicates invalid parameters (only for `setVirtualBackground` method) * `processImage` returns `ImageFrame`: Processed image frame, returns `null` on failure ```java theme={null} public class BeautyEffectEngine { public static void setLogConfig(LogConfig config); public BeautyEffectEngine(Context context, EngineConfig config); public void release(); public int setBeautyParam(BasicParam param, float value); public int setBeautyParam(ReshapeParam param, float value); public int setBeautyParam(MakeupParam param, float value); public int setLipstickStyle(LipstickStyle style); public int setBlushStyle(BlushStyle style); public int setSkinOnlyBeauty(boolean enabled); public int setVirtualBackground(VirtualBackgroundOptions options); public int setFilter(String filterId); public int setFilterIntensity(float intensity); public int setSticker(String stickerId); public int registerFilter(String filterId, String fbdFilePath); public int registerFilter(String filterId, byte[] fbdData); public int registerSticker(String stickerId, String fbdFilePath); public int registerSticker(String stickerId, byte[] fbdData); public int unregisterFilter(String filterId); public int unregisterAllFilters(); public int unregisterSticker(String stickerId); public int unregisterAllStickers(); public String[] getRegisteredFilters(); public String[] getRegisteredStickers(); public void setEngineEventCallback(OnEngineEventCallback callback); public ImageFrame processImage(ImageFrame inputFrame); // Deprecated APIs public int enableBeautyType(BeautyType type, boolean enabled); public boolean isBeautyTypeEnabled(BeautyType type); public int disableAllBeautyTypes(); } ``` ## Beauty Parameters Beauty parameter enumeration classes containing all beauty-related parameter type definitions. ### Beauty Types Define available beauty functionality types. ```java theme={null} public enum BeautyType { BASIC(0), // Basic beauty RESHAPE(1), // Face reshape MAKEUP(2), // Makeup effects VIRTUAL_BACKGROUND(3); // Virtual background } ``` ### Basic Beauty Parameters Basic beauty effect parameter types, all parameter value ranges are `0.0 - 1.0`. ```java theme={null} public enum BasicParam { SMOOTHING(0), // Smoothing SHARPENING(1), // Sharpening WHITENING(2), // Whitening ROSINESS(3); // Rosiness } ``` ### Face Reshape Parameters Face reshape effect parameter types, all parameter value ranges are `0.0 - 1.0`. ```java theme={null} public enum ReshapeParam { FACE_THIN(0), // Face thinning FACE_V_SHAPE(1), // V-shaped face FACE_NARROW(2), // Narrow face FACE_SHORT(3), // Short face CHEEKBONE(4), // Cheekbone JAWBONE(5), // Jawbone CHIN(6), // Chin NOSE_SLIM(7), // Nose slimming EYE_SIZE(8), // Eye enlargement EYE_DISTANCE(9); // Eye distance } ``` ### Makeup Parameters Makeup effect parameter types, all parameter value ranges are `0.0 - 1.0`. ```java theme={null} public enum MakeupParam { LIPSTICK(0), // Lipstick BLUSH(1); // Blush } ``` ### Lipstick Style ```java theme={null} public enum LipstickStyle { ROUGE(0), // Rose red CORAL(1), // Coral PINK(2); // Pink (default) } ``` ### Blush Style ```java theme={null} public enum BlushStyle { CLASSIC(0), // Classic (default) PEACH(1), // Peach ROSE(2); // Rose } ``` ### Virtual Background Options Virtual background effect configuration options. ```java theme={null} public static class VirtualBackgroundOptions { public BackgroundMode mode = BackgroundMode.NONE; public ImageFrame backgroundImage = null; public VirtualBackgroundOptions() {} public VirtualBackgroundOptions(BackgroundMode mode) { this.mode = mode; } } ``` ## Image Related ### ImageFrame Image frame class for encapsulating image data and processing. **Creation Methods:** * `createWithFile(String filePath)`: Create from file (supports PNG, JPG) * `createWithRGBA(ByteBuffer data, int width, int height, int stride)`: Create from RGBA data * `createWithBGRA(ByteBuffer data, int width, int height, int stride)`: Create from BGRA data * `createWithRGB(ByteBuffer data, int width, int height, int stride)`: Create from RGB data * `createWithBGR(ByteBuffer data, int width, int height, int stride)`: Create from BGR data * `createWithI420(...)`: Create from I420 YUV data * `createWithNV12(...)`: Create from NV12 YUV data * `createWithNV21(...)`: Create from NV21 YUV data * `createWithAndroid420(...)`: Create from Android Camera2 YUV\_420\_888 format * `createWithTexture(int texture, int width, int height, int stride)`: Create from GPU texture (external texture input) * `createWithBitmap(Bitmap bitmap)`: Create from Android Bitmap (Android only) **Image Operations:** * `int rotate(Rotation rotation)`: Rotate image (returns 0 for success) * `int mirror(String mode)`: Mirror image (returns 0 for success) * Parameter: `mode` mirror mode, can be "horizontal", "vertical", or "both" (case insensitive) * `void setMirror(String mode)`: Set mirror mode for engine processing (avoids extra format conversion) * Parameter: `mode` mirror mode, can be "horizontal", "vertical", or "both" (case insensitive) * Note: This only sets the mirror flag, actual mirroring happens during engine processing * `ImageFrame convert(Format format)`: Format conversion method, returns converted ImageFrame * `int toFile(String path, int quality)`: Save image to file (specify quality 1-100) * `int toFile(String path)`: Save image to file (use default quality 90) * `boolean isValid()`: Check if image frame is valid **Resource Management:** * `void release()`: Release native resources ```java theme={null} public class ImageFrame { public static ImageFrame createWithFile(String filePath); public static ImageFrame createWithRGBA(ByteBuffer data, int width, int height, int stride); public static ImageFrame createWithBGRA(ByteBuffer data, int width, int height, int stride); public static ImageFrame createWithRGB(ByteBuffer data, int width, int height, int stride); public static ImageFrame createWithBGR(ByteBuffer data, int width, int height, int stride); public static ImageFrame createWithI420(int width, int height, ByteBuffer yBuffer, int strideY, ByteBuffer uBuffer, int strideU, ByteBuffer vBuffer, int strideV); public static ImageFrame createWithNV12(int width, int height, ByteBuffer yBuffer, int strideY, ByteBuffer uvBuffer, int strideUV); public static ImageFrame createWithNV21(int width, int height, ByteBuffer yBuffer, int strideY, ByteBuffer vuBuffer, int strideVU); public static ImageFrame createWithAndroid420(int width, int height, ByteBuffer yBuffer, int strideY, ByteBuffer uBuffer, int strideU, ByteBuffer vBuffer, int strideV, int pixelStrideUV); public static ImageFrame createWithTexture(int texture, int width, int height, int stride); public static ImageFrame createWithBitmap(Bitmap bitmap); public int rotate(Rotation rotation); public int mirror(String mode); public void setMirror(String mode); public ImageFrame convert(Format format); public int toFile(String path, int quality); public int toFile(String path); public boolean isValid(); // Property access methods public int getWidth(); public int getHeight(); public int getStride(); public int getSize(); public ByteBuffer getData(); public Format getFormat(); public int getTexture(); public FrameType type; // Frame type (FrameType.IMAGE or FrameType.VIDEO) // YUV related methods public ByteBuffer getDataY(); public ByteBuffer getDataU(); public ByteBuffer getDataV(); public ByteBuffer getDataUV(); public int getStrideY(); public int getStrideU(); public int getStrideV(); public int getStrideUV(); public void release(); } ``` ### Enum Types #### Format Image format enumeration. ```java theme={null} public enum Format { I420(0), // YUV 4:2:0 12bpp (3 planes, Y, U, V) NV12(1), // YUV 4:2:0 12bpp (2 planes, Y + UV) NV21(2), // YUV 4:2:0 12bpp (2 planes, Y + VU, Android default) BGRA(3), // BGRA 8:8:8:8 32bpp RGBA(4), // RGBA 8:8:8:8 32bpp BGR(5), // BGR 8:8:8 24bpp RGB(6), // RGB 8:8:8 24bpp Texture(7); // Texture format } ``` #### Rotation Image rotation angle enumeration. ```java theme={null} public enum Rotation { ROTATION_0(0), // 0 degrees ROTATION_90(1), // Clockwise 90 degrees ROTATION_180(2), // Clockwise 180 degrees ROTATION_270(3); // Clockwise 270 degrees } ``` ## Deprecated APIs **Deprecated** The following APIs are deprecated. ### Beauty Type Control * `int enableBeautyType(BeautyType type, boolean enabled)` * **Description**: \[Deprecated] Enable or disable beauty type (No effect in parameter-driven mode) * **Return Value**: `0` indicates success * `boolean isBeautyTypeEnabled(BeautyType type)` * **Description**: \[Deprecated] Check if beauty type is enabled (Always returns false) * **Return Value**: `false` * `int disableAllBeautyTypes()` * **Description**: \[Deprecated] Disable all beauty types (Please reset effects by zeroing parameters) * **Return Value**: `0` indicates success # Best Practices Source: https://facebetter.mintlify.app/android/best-practices Android Beauty SDK Best Practices Guide ## Performance Optimization ### 1. Choose the Right Processing Mode **Video Mode (VIDEO)** * Suitable for real-time video streams and live streaming scenarios * Better performance and faster processing speed * Recommended for camera preview, video calls, and similar scenarios **Image Mode (IMAGE)** * Suitable for single image processing * Higher quality and better effects * Recommended for photo editing and image beautification scenarios ```java theme={null} // Real-time video processing input.type = ImageFrame.FrameType.VIDEO; ImageFrame output = engine.processImage(input); // High-quality image processing input.type = ImageFrame.FrameType.IMAGE; ImageFrame output = engine.processImage(input); ``` ### 2. Parameter Adjustment Recommendations **Beauty Parameter Adjustment Principles** * Start with smaller values to avoid over-beautification * Recommend reducing parameter values in real-time scenarios for smooth performance * Static images can have appropriately higher parameter values * Adjust parameter ranges based on user groups and scenarios **Recommended Parameter Ranges** ```java theme={null} // Basic beauty parameters (real-time scenarios) mBeautyEngine.setBeautyParam(BasicParam.SMOOTHING, 0.2f); // Smoothing mBeautyEngine.setBeautyParam(BasicParam.WHITENING, 0.1f); // Whitening mBeautyEngine.setBeautyParam(BasicParam.ROSINESS, 0.1f); // Rosiness // Face reshaping parameters (real-time scenarios) mBeautyEngine.setBeautyParam(ReshapeParam.FACE_THIN, 0.1f); // Face thinning mBeautyEngine.setBeautyParam(ReshapeParam.EYE_SIZE, 0.1f); // Eye enlargement ``` ### 3. Memory Optimization **Use Direct Memory Buffers** ```java theme={null} // Recommended: Use direct memory ByteBuffer data = ByteBuffer.allocateDirect(width * height * 4); // Avoid: Use heap memory ByteBuffer data = ByteBuffer.allocate(width * height * 4); ``` **Release Resources Timely** ```java theme={null} public class BeautyProcessor { private ImageFrame mReusableFrame; // Reusable object public ImageFrame processImage(byte[] imageData) { // Reuse ImageFrame object if (mReusableFrame == null) { mReusableFrame = ImageFrame.createWithRGBA(data, width, height, stride); } // Process image mReusableFrame.type = ImageFrame.FrameType.VIDEO; ImageFrame output = mBeautyEngine.processImage(mReusableFrame); return output; } public void release() { if (mReusableFrame != null) { mReusableFrame.release(); mReusableFrame = null; } } } ``` ## Architecture Design ### 1. Singleton Pattern for Engine Management ```java theme={null} public class BeautyEngineManager { private static BeautyEngineManager sInstance; private BeautyEffectEngine mEngine; private Context mContext; private BeautyEngineManager(Context context) { mContext = context.getApplicationContext(); initEngine(); } public static synchronized BeautyEngineManager getInstance(Context context) { if (sInstance == null) { sInstance = new BeautyEngineManager(context); } return sInstance; } private void initEngine() { BeautyEffectEngine.EngineConfig config = new BeautyEffectEngine.EngineConfig(); config.appId = "your_app_id"; config.appKey = "your_app_key"; mEngine = new BeautyEffectEngine(mContext, config); } public BeautyEffectEngine getEngine() { return mEngine; } public void release() { if (mEngine != null) { mEngine.release(); mEngine = null; } sInstance = null; } } ``` ### 2. Asynchronous Image Processing ```java theme={null} public class AsyncBeautyProcessor { private ExecutorService mExecutor; private BeautyEffectEngine mEngine; public AsyncBeautyProcessor() { mExecutor = Executors.newSingleThreadExecutor(); } public void processImageAsync(ImageFrame input, BeautyCallback callback) { mExecutor.execute(() -> { try { input.type = ImageFrame.FrameType.VIDEO; ImageFrame output = mEngine.processImage(input); // Switch to main thread for callback new Handler(Looper.getMainLooper()).post(() -> { callback.onSuccess(output); }); } catch (Exception e) { new Handler(Looper.getMainLooper()).post(() -> { callback.onError(e); }); } }); } public interface BeautyCallback { void onSuccess(ImageFrame result); void onError(Exception error); } } ``` ## Error Handling ### 1. Comprehensive Error Handling Mechanism ```java theme={null} public class RobustBeautyProcessor { private BeautyEffectEngine mEngine; public boolean processImage(ImageFrame input, ImageFrame output) { // Parameter validation if (mEngine == null) { Log.e("BeautyProcessor", "Engine not initialized"); return false; } if (input == null || !input.isValid()) { Log.e("BeautyProcessor", "Invalid input image"); return false; } try { // Process image ImageFrame result = mEngine.processImage(input, ProcessMode.VIDEO); if (result == null) { Log.e("BeautyProcessor", "Failed to process image"); return false; } // Copy result to output copyImageFrame(result, output); result.release(); return true; } catch (Exception e) { Log.e("BeautyProcessor", "Exception during processing", e); return false; } } private void copyImageFrame(ImageFrame src, ImageFrame dst) { // Implement image copying logic } } ``` ### 2. Retry Mechanism ```java theme={null} public class RetryBeautyProcessor { private static final int MAX_RETRY_COUNT = 3; private static final long RETRY_DELAY_MS = 100; public ImageFrame processImageWithRetry(ImageFrame input) { for (int i = 0; i < MAX_RETRY_COUNT; i++) { try { ImageFrame result = mBeautyEngine.processImage(input, ProcessMode.VIDEO); if (result != null) { return result; } } catch (Exception e) { Log.w("BeautyProcessor", "Attempt " + (i + 1) + " failed", e); } if (i < MAX_RETRY_COUNT - 1) { try { Thread.sleep(RETRY_DELAY_MS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } Log.e("BeautyProcessor", "All retry attempts failed"); return null; } } ``` ## Lifecycle Management The beauty engine requires manual `release()` to free resources. ImageFrame and other objects also need to call `release()` after use, otherwise it will cause memory leaks. ### 1. Activity Lifecycle Handling ```java theme={null} public class BeautyActivity extends AppCompatActivity { private BeautyEngineManager mBeautyManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Initialize beauty engine mBeautyManager = BeautyEngineManager.getInstance(this); } @Override protected void onResume() { super.onResume(); // Resume beauty processing } @Override protected void onPause() { super.onPause(); // Pause beauty processing } @Override protected void onDestroy() { super.onDestroy(); // Release beauty engine (if no longer needed) // mBeautyManager.release(); } } ``` ### 2. Fragment Lifecycle Handling ```java theme={null} public class BeautyFragment extends Fragment { private BeautyEffectEngine mBeautyEngine; @Override public void onAttach(Context context) { super.onAttach(context); // Initialize beauty engine initBeautyEngine(); } @Override public void onDetach() { super.onDetach(); // Release beauty engine if (mBeautyEngine != null) { mBeautyEngine.release(); mBeautyEngine = null; } } } ``` ## Performance Monitoring ### 1. Performance Metrics Monitoring ```java theme={null} public class BeautyPerformanceMonitor { private long mStartTime; private int mFrameCount; private long mTotalProcessTime; public void startFrame() { mStartTime = System.currentTimeMillis(); } public void endFrame() { long processTime = System.currentTimeMillis() - mStartTime; mTotalProcessTime += processTime; mFrameCount++; // Calculate average processing time if (mFrameCount % 30 == 0) { // Calculate every 30 frames long avgTime = mTotalProcessTime / mFrameCount; Log.d("BeautyPerformance", "Average process time: " + avgTime + "ms"); // Reset counters mTotalProcessTime = 0; mFrameCount = 0; } } public boolean isPerformanceGood() { return mTotalProcessTime / Math.max(mFrameCount, 1) < 33; // 30fps } } ``` ### 2. Memory Usage Monitoring ```java theme={null} public class MemoryMonitor { private Runtime mRuntime; public MemoryMonitor() { mRuntime = Runtime.getRuntime(); } public void logMemoryUsage(String tag) { long totalMemory = mRuntime.totalMemory(); long freeMemory = mRuntime.freeMemory(); long usedMemory = totalMemory - freeMemory; long maxMemory = mRuntime.maxMemory(); Log.d("MemoryMonitor", tag + " - Used: " + (usedMemory / 1024 / 1024) + "MB, " + "Max: " + (maxMemory / 1024 / 1024) + "MB"); } public boolean isMemoryLow() { long usedMemory = mRuntime.totalMemory() - mRuntime.freeMemory(); long maxMemory = mRuntime.maxMemory(); return usedMemory > maxMemory * 0.8; // Over 80% usage } } ``` ## Configuration Management ### 1. Beauty Configuration Management ```java theme={null} public class BeautyConfigManager { private SharedPreferences mPrefs; private static final String PREF_NAME = "beauty_config"; public BeautyConfigManager(Context context) { mPrefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); } public void saveBeautyConfig(BeautyConfig config) { SharedPreferences.Editor editor = mPrefs.edit(); editor.putFloat("smoothing", config.smoothing); editor.putFloat("whitening", config.whitening); editor.putFloat("face_thin", config.faceThin); editor.putBoolean("basic_enabled", config.basicEnabled); editor.putBoolean("reshape_enabled", config.reshapeEnabled); editor.apply(); } public BeautyConfig loadBeautyConfig() { BeautyConfig config = new BeautyConfig(); config.smoothing = mPrefs.getFloat("smoothing", 0.3f); config.whitening = mPrefs.getFloat("whitening", 0.2f); config.faceThin = mPrefs.getFloat("face_thin", 0.1f); config.basicEnabled = mPrefs.getBoolean("basic_enabled", true); config.reshapeEnabled = mPrefs.getBoolean("reshape_enabled", false); return config; } public static class BeautyConfig { public float smoothing = 0.3f; public float whitening = 0.2f; public float faceThin = 0.1f; public boolean basicEnabled = true; public boolean reshapeEnabled = false; } } ``` ## Testing Recommendations ### 1. Unit Testing ```java theme={null} @Test public void testBeautyEngineInitialization() { BeautyEffectEngine.EngineConfig config = new BeautyEffectEngine.EngineConfig(); config.appId = "test_app_id"; config.appKey = "test_app_key"; BeautyEffectEngine engine = new BeautyEffectEngine(mContext, config); assertNotNull("Engine should be created", engine); engine.release(); } @Test public void testImageProcessing() { // Create test image ByteBuffer data = ByteBuffer.allocateDirect(640 * 480 * 4); ImageFrame input = ImageFrame.createWithRGBA(data, 640, 480, 640 * 4); // Process image ImageFrame output = mBeautyEngine.processImage(input, ProcessMode.VIDEO); assertNotNull("Output should not be null", output); assertTrue("Output should be valid", output.isValid()); input.release(); output.release(); } ``` ### 2. Performance Testing ```java theme={null} @Test public void testPerformance() { long startTime = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { // Process test image processTestImage(); } long endTime = System.currentTimeMillis(); long totalTime = endTime - startTime; long avgTime = totalTime / 100; assertTrue("Average process time should be less than 50ms", avgTime < 50); } ``` # Error Handling Source: https://facebetter.mintlify.app/android/error-handling Android Beauty SDK Error Handling Guide ## Error Code Handling ### 1. Check API Return Values All API calls should check return values: ```java theme={null} // Check engine creation BeautyEffectEngine.EngineConfig config = new BeautyEffectEngine.EngineConfig(); config.appId = "your_app_id"; config.appKey = "your_app_key"; // Optional: If licenseJson is provided, license data verification takes priority // config.licenseJson = "your license json string"; mBeautyEngine = new BeautyEffectEngine(this, config); if (mBeautyEngine == null) { Log.e("BeautyEngine", "Failed to create beauty engine"); return; } // Check parameter setting int ret = mBeautyEngine.setBeautyParam(BasicParam.SMOOTHING, 0.5f); if (ret == 0) { Log.d("BeautyEngine", "Successfully set basic beauty parameters"); } else { Log.e("BeautyEngine", "Failed to set smoothing param, error code: " + ret); } ``` ### 2. Common Error Codes According to API documentation, main error codes: * **0**: Success * **-1**: Engine not initialized ```java theme={null} public class BeautyErrorHandler { public static void handleError(int errorCode, String operation) { switch (errorCode) { case 0: Log.d("BeautyErrorHandler", operation + " succeeded"); break; case -1: Log.e("BeautyErrorHandler", operation + " failed: Engine not initialized"); break; default: Log.e("BeautyErrorHandler", operation + " failed: Unknown error code " + errorCode); break; } } } ``` ## Handle Image Data ### 1. Input Image Validation ```java theme={null} public class ImageValidator { public static boolean validateImageFrame(ImageFrame imageFrame) { if (imageFrame == null) { Log.e("ImageValidator", "ImageFrame is null"); return false; } // Check image dimensions int width = imageFrame.getWidth(); int height = imageFrame.getHeight(); if (width <= 0 || height <= 0) { Log.e("ImageValidator", "Invalid image dimensions: " + width + "x" + height); return false; } return true; } } ``` ### 2. Image Processing Error Handling ```java theme={null} public class ImageProcessor { private BeautyEffectEngine mEngine; public ImageFrame processImageSafely(ImageFrame input) { // Validate input if (!ImageValidator.validateImageFrame(input)) { return null; } try { // Process image input.type = ImageFrame.FrameType.VIDEO; ImageFrame output = mEngine.processImage(input); if (output == null) { Log.e("ImageProcessor", "Failed to process image: output is null"); return null; } return output; } catch (Exception e) { Log.e("ImageProcessor", "Exception during image processing", e); return null; } } } ``` ### 3. Memory Management ```java theme={null} public class SafeImageProcessor { public boolean processImageSafe(ImageFrame input, ImageFrame output) { ImageFrame result = null; try { // Process image result = mEngine.processImage(input, ProcessMode.VIDEO); if (result == null) { Log.e("SafeImageProcessor", "Processing returned null"); return false; } // Copy result to output copyImageFrame(result, output); return true; } catch (OutOfMemoryError e) { Log.e("SafeImageProcessor", "Out of memory during processing", e); System.gc(); // Trigger garbage collection return false; } catch (Exception e) { Log.e("SafeImageProcessor", "Exception during processing", e); return false; } finally { // Ensure resource release if (result != null) { result.release(); } } } private void copyImageFrame(ImageFrame src, ImageFrame dst) { // Implement image copying logic ImageBuffer srcBuffer = src.toRGBA(); ImageBuffer dstBuffer = dst.toRGBA(); if (srcBuffer != null && dstBuffer != null) { ByteBuffer srcData = srcBuffer.getData(); ByteBuffer dstData = dstBuffer.getData(); if (srcData != null && dstData != null) { dstData.put(srcData); } } if (srcBuffer != null) { srcBuffer.release(); } if (dstBuffer != null) { dstBuffer.release(); } } } ``` ## Logging and File Operations ### 1. Log Configuration Error Handling ```java theme={null} public class LogConfigHandler { public static boolean configureLogging(boolean enableConsole, boolean enableFile, String logFilePath) { try { BeautyEffectEngine.LogConfig logConfig = new BeautyEffectEngine.LogConfig(); logConfig.consoleEnabled = enableConsole; logConfig.fileEnabled = enableFile; logConfig.level = BeautyEffectEngine.LogLevel.INFO; if (enableFile && logFilePath != null && !logFilePath.isEmpty()) { // Check if directory exists File logFile = new File(logFilePath); File logDir = logFile.getParentFile(); if (logDir != null && !logDir.exists()) { if (!logDir.mkdirs()) { Log.e("LogConfigHandler", "Failed to create log directory: " + logDir.getPath()); return false; } } logConfig.fileName = logFilePath; } BeautyEffectEngine.setLogConfig(logConfig); return true; } catch (Exception e) { Log.e("LogConfigHandler", "Failed to configure logging", e); return false; } } } ``` ### 2. File Write Error Handling ```java theme={null} public class FileWriteHandler { public static boolean saveImageBuffer(ImageBuffer buffer, String filePath) { if (buffer == null) { Log.e("FileWriteHandler", "ImageBuffer is null"); return false; } FileOutputStream fos = null; try { File file = new File(filePath); File parentDir = file.getParentFile(); // Create directory if (parentDir != null && !parentDir.exists()) { if (!parentDir.mkdirs()) { Log.e("FileWriteHandler", "Failed to create directory: " + parentDir.getPath()); return false; } } // Write file fos = new FileOutputStream(file); ByteBuffer data = buffer.getData(); if (data != null) { byte[] array = new byte[data.remaining()]; data.get(array); fos.write(array); fos.flush(); } Log.d("FileWriteHandler", "Image saved successfully: " + filePath); return true; } catch (IOException e) { Log.e("FileWriteHandler", "IO error while saving file: " + filePath, e); return false; } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { Log.e("FileWriteHandler", "Failed to close file stream", e); } } } } } ``` ## Exception Handling ### 1. Catch and Handle Exceptions ```java theme={null} public class ExceptionHandler { public static void safeExecute(Runnable runnable, String operation) { try { runnable.run(); } catch (NullPointerException e) { Log.e("ExceptionHandler", operation + " failed: Null pointer exception", e); } catch (IllegalArgumentException e) { Log.e("ExceptionHandler", operation + " failed: Invalid argument", e); } catch (OutOfMemoryError e) { Log.e("ExceptionHandler", operation + " failed: Out of memory", e); System.gc(); // Trigger garbage collection } catch (Exception e) { Log.e("ExceptionHandler", operation + " failed: Unexpected exception", e); } } } ``` ### 2. Retry Mechanism ```java theme={null} public class RetryHandler { private static final int MAX_RETRY_COUNT = 3; private static final long RETRY_DELAY_MS = 100; public ImageFrame processImageWithRetry(ImageFrame input) { for (int i = 0; i < MAX_RETRY_COUNT; i++) { try { ImageFrame result = mBeautyEngine.processImage(input, ProcessMode.VIDEO); if (result != null) { return result; } } catch (Exception e) { Log.w("RetryHandler", "Attempt " + (i + 1) + " failed", e); } if (i < MAX_RETRY_COUNT - 1) { try { Thread.sleep(RETRY_DELAY_MS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } Log.e("RetryHandler", "All retry attempts failed"); return null; } } ``` ## Debugging Tips ### 1. Error Information Collection ```java theme={null} public class DebugInfoCollector { public static String collectErrorInfo(int errorCode, String operation, Exception e) { StringBuilder info = new StringBuilder(); info.append("Operation: ").append(operation).append("\n"); info.append("Error Code: ").append(errorCode).append("\n"); info.append("Timestamp: ").append(System.currentTimeMillis()).append("\n"); if (e != null) { info.append("Exception: ").append(e.getClass().getName()).append("\n"); info.append("Message: ").append(e.getMessage()).append("\n"); } // Add memory information Runtime runtime = Runtime.getRuntime(); info.append("Memory - Total: ").append(runtime.totalMemory() / 1024 / 1024).append("MB\n"); info.append("Memory - Free: ").append(runtime.freeMemory() / 1024 / 1024).append("MB\n"); return info.toString(); } } ``` ### 2. Performance Monitoring ```java theme={null} public class PerformanceMonitor { private long mStartTime; private int mErrorCount; public void startOperation() { mStartTime = System.currentTimeMillis(); } public void endOperation(String operation, boolean success) { long duration = System.currentTimeMillis() - mStartTime; if (!success) { mErrorCount++; Log.w("PerformanceMonitor", String.format("%s failed after %dms (Error count: %d)", operation, duration, mErrorCount)); } else if (duration > 100) { Log.w("PerformanceMonitor", String.format("%s took %dms (slow operation)", operation, duration)); } } } ``` ## Summary Following these error handling best practices can help you: 1. **Improve App Stability**: Through API return value checking and exception handling 2. **Improve User Experience**: Through friendly error prompts and recovery mechanisms 3. **Facilitate Problem Troubleshooting**: Through detailed logging and error information collection 4. **Optimize Performance**: Through reasonable error recovery strategies and resource management Remember to adjust error handling logic based on actual SDK error code definitions and continuously monitor and improve error handling mechanisms. # FAQ Source: https://facebetter.mintlify.app/android/faq Android Beauty SDK Common Questions and Answers ## Integration Issues ### Q: What to do if engine creation fails? A: Check the following points: * Confirm that `appId` and `appKey` are correct * Check if network connection is normal * View log output for detailed error information * Confirm if SDK version is latest ### Q: Can't find classes or methods during compilation? A: Possible reasons: * Confirm that `facebetter.aar` dependency has been properly added * Check if `build.gradle` configuration is correct * Try Clean Project and rebuild * Confirm using the correct package name `net.pixpark.facebetter` ### Q: UnsatisfiedLinkError at runtime? A: Solutions: * Confirm if device architecture is supported (arm64-v8a, armeabi-v7a) * Check if AAR file is complete * Confirm native methods are not obfuscated ## Functionality Issues ### Q: Beauty effects are not obvious? A: You can try: * Increase beauty parameter values (range 0.0-1.0) * Ensure corresponding beauty types are enabled * Check if image quality is clear enough * Confirm if face detection is working normally ### Q: Beauty effects are excessive or distorted? A: Recommendations: * Reduce beauty parameter values * Check if parameter combinations are reasonable * Avoid enabling too many beauty types simultaneously * Adjust parameters based on image quality ### Q: Virtual background not working? A: Check: * Confirm `BeautyType.VIRTUAL_BACKGROUND` is enabled * Check if background image path is correct * Confirm if image format is supported (PNG, JPG) * Check if image file exists and is readable ## Performance Issues ### Q: What to do if processing is slow? A: Optimization suggestions: * Use VIDEO mode for real-time processing * Reduce image resolution * Reduce number of simultaneously enabled beauty types * Release unused image objects timely * Avoid image processing on main thread ### Q: High memory usage? A: Solutions: * Use `ByteBuffer.allocateDirect()` to create direct memory buffers * Call `release()` method timely to release resources * Avoid frequent creation and destruction of engine instances * Reuse `ImageFrame` objects ### Q: App crashes or ANR? A: Troubleshooting steps: * Check if image processing is done on main thread * Confirm resource release is complete * View exception information in logs * Check if parameter values are within valid range ## Image Processing Issues ### Q: How to handle different image formats? A: Use `ImageFrame`'s format conversion methods: * `toRGBA()` - Convert to RGBA format * `toI420()` - Convert to I420 format * `toNV21()` - Convert to NV21 format (Android camera default format) * `toBGRA()` - Convert to BGRA format ### Q: Camera preview image processing? A: Recommended process: 1. Get YUV data from camera 2. Use `createWithAndroid420()` to create ImageFrame 3. Call `processImage()` to process 4. Convert to target format for display ### Q: Image rotation issues? A: Solutions: * Use `ImageFrame.rotate()` method to rotate images * Supports 0°, 90°, 180°, 270° rotation * Rotation operation modifies original image data ## Permission Issues ### Q: File access permission issues? A: Need to add permissions: ```xml theme={null} ``` ### Q: Camera permission issues? A: Need to add permissions: ```xml theme={null} ``` ## Debugging Issues ### Q: How to enable debug logging? A: Configure before creating engine: ```java theme={null} BeautyEffectEngine.LogConfig logConfig = new BeautyEffectEngine.LogConfig(); logConfig.consoleEnabled = true; logConfig.fileEnabled = true; logConfig.level = BeautyEffectEngine.LogLevel.DEBUG; BeautyEffectEngine.setLogConfig(logConfig); ``` ### Q: How to get detailed error information? A: Methods: * Enable DEBUG level logging * Check API return values (0 indicates success) * View file log output * Use Android Studio's Logcat to view logs ## Version Compatibility ### Q: Which Android versions are supported? A: Supports Android 5.0 (API 21) and above ### Q: Which device architectures are supported? A: Supports: * arm64-v8a (64-bit ARM) * armeabi-v7a (32-bit ARM) ### Q: How to upgrade SDK version? A: Steps: 1. Download new version SDK 2. Replace old AAR file 3. Clean project cache 4. Rebuild project 5. Test if functionality works normally # Implement Beauty Source: https://facebetter.mintlify.app/android/implement-beauty Implement Android Beauty ## Add SDK Dependency ### Method A: Maven Online Integration (Recommended) Add the Maven repository address to your project's root `build.gradle` or `settings.gradle`: ```groovy theme={null} repositories { mavenCentral() } ``` Add the dependency in your module's `build.gradle`: ```groovy theme={null} dependencies { implementation 'net.pixpark:facebetter:1.2.2' } ``` Alternatively, if you use the modern **Version Catalog** (`libs.versions.toml`) to manage dependencies (recommended, as used in the Demo): 1. Add the following to `gradle/libs.versions.toml`: ```toml theme={null} [versions] facebetter = "1.2.2" [libraries] facebetter = { group = "net.pixpark", name = "facebetter", version.ref = "facebetter" } ``` 2. Use it in your module's `build.gradle`: ```groovy theme={null} dependencies { implementation libs.facebetter } ``` ### Method B: Manual AAR Integration Go to the [Download](https://facebetter.net/download) page to get the latest SDK, then extract it. Copy the `facebetter.aar` library from the SDK package to your project path, such as the `libs` directory. Modify the project's `build.gradle` and add the `facebetter.aar` dependency in the `dependencies` section: ```groovy theme={null} dependencies { implementation files('libs/facebetter.aar') implementation libs.appcompat implementation libs.material } ``` ### Permission Configuration Add necessary permissions in `AndroidManifest.xml`: ```xml theme={null} ``` **Permission Descriptions:** * **Network Permission**: Required. SDK needs network connection to verify `appId` and `appKey` to ensure the app runs normally. * **Storage Permission**: Optional. Only needed when file logging is configured (`logConfig.fileEnabled = true`). * **Camera Permission**: Optional. Only needed when using camera capture for beauty processing in the app. Not required if only processing existing images. ## Import Classes Facebetter engine mainly has four classes that need to be imported into the files where they are used: ```java theme={null} import net.pixpark.facebetter.BeautyEffectEngine; import net.pixpark.facebetter.BeautyParams.*; import net.pixpark.facebetter.ImageBuffer; import net.pixpark.facebetter.ImageFrame; ``` ## Log Configuration Logging is disabled by default and can be enabled as needed. Both console logging and file logging switches are supported. Logging should be enabled **before** creating the beauty engine, otherwise you may not see initialization logs. ```java theme={null} BeautyEffectEngine.LogConfig logConfig = new BeautyEffectEngine.LogConfig(); // Console logging logConfig.consoleEnabled = true; // File logging logConfig.fileEnabled = true; // Log level logConfig.level = BeautyEffectEngine.LogLevel.INFO; // Log storage path logConfig.fileName = "xx/xx/facebetter.log"; BeautyEffectEngine.setLogConfig(logConfig); ``` ## Create Configuration Engine Follow the instructions on [this page](/intro/enable-service#get-appid-and-appkey) to get your `appid` and `appkey`. **Verification Priority:** * If `licenseJson` is provided, use license data verification (supports online response and offline license) * Otherwise, use `appId` and `appKey` for automatic online verification ```java theme={null} BeautyEffectEngine.EngineConfig config = new BeautyEffectEngine.EngineConfig(); config.appId = "your appId"; // Configure your appid (optional, not required if licenseJson is provided) config.appKey = "your appkey"; // Configure your appkey (optional, not required if licenseJson is provided) // Optional: Use license data verification (takes priority if provided) // config.licenseJson = "your license json string"; mBeautyEngine = new BeautyEffectEngine(this, config); ``` ### Error Handling After creating the engine, it's recommended to check if it was successful: ```java theme={null} if (mBeautyEngine == null) { Log.e("BeautyEngine", "Failed to create beauty engine"); return; } ``` ## Adjust Beauty Parameters All beauty parameters range from `[0.0, 1.0]`. Set to `0` to disable the effect. ### Set Skin Beauty Parameters Use the `setBeautyParam` interface to set skin beauty parameters. **Parameter range \[0.0, 1.0]**. ```java theme={null} float value = 0.5; // Value range [0.0, 1.0] int ret = mBeautyEngine.setBeautyParam(BasicParam.SMOOTHING, value); ``` Supported skin beauty parameters: ```java theme={null} public static enum BasicParam { SMOOTHING(0), // Smoothing SHARPENING(1), // Sharpening WHITENING(2), // Whitening ROSINESS(3); // Rosiness } ``` ### Set Skin-Only Beauty Use the `setSkinOnlyBeauty` interface to set whether beauty effects are applied only to skin regions. When enabled, beauty effects (smoothing, whitening, etc.) will only be applied to detected skin areas, leaving non-skin areas unchanged. ```java theme={null} // Enable skin-only beauty int ret = mBeautyEngine.setSkinOnlyBeauty(true); // Disable skin-only beauty (apply to entire image) ret = mBeautyEngine.setSkinOnlyBeauty(false); ``` After enabling skin-only beauty, even with high beauty parameter values, non-skin areas (such as background, clothing, etc.) will not be affected. ### Set Face Reshape Parameters Use the `setBeautyParam` interface to set face reshape parameters. **Parameter range \[0.0, 1.0]**. ```java theme={null} beautyEffectEngine.setBeautyParam(BeautyParams.ReshapeParam.FACE_THIN, 0.5f); ``` Supported face reshape parameters: ```java theme={null} public enum ReshapeParam { FACE_THIN(0), // Face thinning FACE_V_SHAPE(1), // V-shaped face FACE_NARROW(2), // Narrow face FACE_SHORT(3), // Short face CHEEKBONE(4), // Cheekbone JAWBONE(5), // Jawbone CHIN(6), // Chin NOSE_SLIM(7), // Nose slimming EYE_SIZE(8), // Eye enlargement EYE_DISTANCE(9); // Eye distance } ``` ### Set Makeup Parameters ```java theme={null} beautyEffectEngine.setBeautyParam(BeautyParams.MakeupParam.LIPSTICK, 0.5f); ``` Supported makeup parameters: ```java theme={null} public enum MakeupParam { LIPSTICK(0), // Lipstick BLUSH(1); // Blush } ``` ### Set Virtual Background Enable virtual background through the `setVirtualBackground` interface: ```java theme={null} // Set background mode BeautyParams.VirtualBackgroundOptions options = new BeautyParams.VirtualBackgroundOptions(); options.mode = BeautyParams.BackgroundMode.BLUR; // Blur background beautyEffectEngine.setVirtualBackground(options); // Set background image (need to set to IMAGE mode first) BeautyParams.VirtualBackgroundOptions imageOptions = new BeautyParams.VirtualBackgroundOptions(); imageOptions.mode = BeautyParams.BackgroundMode.IMAGE; imageOptions.backgroundImage = imageFrame; // ImageFrame object beautyEffectEngine.setVirtualBackground(imageOptions); ``` ## Using Filters and Stickers ### Filter Functionality Filters are set through the `setFilter` interface. Filter resource files (`.fbd`) must be registered via `registerFilter` first. ```java theme={null} // 1. Register filter resource String filterId = "chuxin"; String fbdPath = getFilesDir() + "/filters/chuxin.fbd"; mBeautyEngine.registerFilter(filterId, fbdPath); // 2. Use filter mBeautyEngine.setFilter(filterId); // 3. Adjust filter intensity (0.0 - 1.0) mBeautyEngine.setFilterIntensity(0.8f); ``` ### Sticker Functionality Stickers are set through the `setSticker` interface and also need to be registered first. ```java theme={null} // 1. Register sticker resource String stickerId = "cherry"; String fbdPath = getFilesDir() + "/stickers/cherry.fbd"; mBeautyEngine.registerSticker(stickerId, fbdPath); // 2. Use sticker mBeautyEngine.setSticker(stickerId); // 3. Clear sticker mBeautyEngine.setSticker(""); ``` ## Set Callbacks Monitor engine events (license validation and engine initialization status): ```java theme={null} EngineCallbacks callbacks = new EngineCallbacks(); callbacks.onEngineEvent = (code, message) -> { if (code == EngineEventCode.LICENSE_VALIDATION_SUCCESS) { // License validation succeeded Log.d(TAG, "License validation succeeded"); } else if (code == EngineEventCode.LICENSE_VALIDATION_FAILED) { // License validation failed Log.e(TAG, "License validation failed: " + message); } else if (code == EngineEventCode.INITIALIZATION_COMPLETE) { // Engine initialization completed Log.d(TAG, "Engine initialization completed"); } else if (code == EngineEventCode.INITIALIZATION_FAILED) { // Engine initialization failed Log.e(TAG, "Engine initialization failed: " + message); } }; beautyEffectEngine.setCallbacks(callbacks); ``` Event codes: * `EngineEventCode.LICENSE_VALIDATION_SUCCESS` (0): License validation succeeded * `EngineEventCode.LICENSE_VALIDATION_FAILED` (1): License validation failed * `EngineEventCode.INITIALIZATION_COMPLETE` (100): Engine initialization completed * `EngineEventCode.INITIALIZATION_FAILED` (101): Engine initialization failed ## Process Images ImageFrame objects must call `release()` after use, otherwise it will cause memory leaks. ### Create Images Image data is encapsulated through `ImageFrame`, supporting formats: `I420`, `NV12`, `NV21`, `RGB`, `RGBA`, `BGR`, `BGRA`. **Create ImageFrame with RGBA** ```java theme={null} ByteBuffer data = ByteBuffer.allocateDirect(width * height * 4); ImageFrame inputImage = ImageFrame.createWithRGBA(data, width, height, stride); ``` **Create ImageFrame with image file** ```java theme={null} ImageFrame inputImage = ImageFrame.createWithFile("xxx.png"); ``` ### Rotate Images ImageFrame has built-in image rotation methods that can be used as needed. ```java theme={null} int result = inputImage.rotate(ImageBuffer.Rotation.ROTATION_90); ``` Rotation angles ```java theme={null} public enum Rotation { ROTATION_0(0), // 0 degrees ROTATION_90(1), // Clockwise 90 degrees ROTATION_180(2), // Clockwise 180 degrees ROTATION_270(3); // Clockwise 270 degrees } ``` ### Process Images `processMode` includes VIDEO and IMAGE modes. VIDEO mode is suitable for live streaming and video scenarios with higher efficiency. IMAGE mode is suitable for image processing scenarios. ```java theme={null} inputImage.type = ImageFrame.FrameType.VIDEO; ImageFrame outputImage = beautyEffectEngine.processImage(inputImage); ``` The engine automatically maintains input/output format consistency. If input is NV21 format, output is NV21 format; if input is RGBA format, output is RGBA format. ### Get Processed Image Data ```java theme={null} ImageBuffer buffer = outputImage.toRGBA(); ByteBuffer data = buffer.getData(); int dataSize = buffer.getSize(); int width = buffer.getWidth(); int height = buffer.getHeight(); int stride = buffer.getStride(); ``` Get I420 data ```java theme={null} ImageBuffer buffer = outputImage.toI420(); // Get continuous I420 memory data ByteBuffer data = buffer.getData(); // Get I420 data length int dataSize = buffer.getSize(); // Get Y, U, V component data separately ByteBuffer dataY = buffer.getDataY(); ByteBuffer dataU = buffer.getDataU(); ByteBuffer dataV = buffer.getDataV(); int strideY = buffer.getStrideY(); int strideU = buffer.getStrideU(); int strideV = buffer.getStrideV(); ``` `ImageFrame` can be converted to various formats through built-in toXXX methods: `I420`, `NV12`, `NV21`, `RGB`, `RGBA`, `BGR`, `BGRA`. These methods can be used for format conversion. ## External Texture Processing When using external texture processing, you must ensure the OpenGL context is on the main thread and pass `externalContext = true` during engine initialization. ### Use Cases External texture processing is suitable for the following scenarios: * **OpenGL/OpenGL ES Rendering Pipeline Integration**: When your application already uses OpenGL for rendering, you can directly use textures as input and output, avoiding CPU-GPU data copying * **Real-time Video Processing**: Process textures directly in video rendering callbacks to reduce memory copy overhead * **Performance Optimization**: Avoid downloading texture data to CPU memory and uploading back to GPU, improving processing efficiency ### Configure External Context When using external texture processing, you need to enable the `externalContext` option when creating the engine: ```java theme={null} BeautyEffectEngine.EngineConfig config = new BeautyEffectEngine.EngineConfig(); config.appId = "your appId"; config.appKey = "your appKey"; config.externalContext = true; // Enable external context mode BeautyEffectEngine engine = new BeautyEffectEngine(context, config); ``` **Important Notes**: * When `externalContext = true`, the engine will not create its own OpenGL context, but use the current thread's OpenGL context * The engine must be created in a valid OpenGL context * Input and output textures must be in the same OpenGL context ### Create Texture Frame Use the `ImageFrame.createWithTexture()` method to create an image frame from an OpenGL texture: ```java theme={null} // Create ImageFrame from OpenGL texture int textureId = ...; // Your OpenGL texture ID int width = 1920; int height = 1080; int stride = width * 4; // RGBA format, 4 bytes per pixel ImageFrame inputFrame = ImageFrame.createWithTexture(textureId, width, height, stride); if (inputFrame == null) { Log.e(TAG, "Failed to create ImageFrame from texture"); return; } ``` **Parameter Description**: * `textureId`: OpenGL texture ID (type `GL_TEXTURE_2D`) * `width`: Texture width (pixels) * `height`: Texture height (pixels) * `stride`: Row stride (bytes), usually `width * 4` (RGBA format) ### Get Output Texture After processing the image, you can get the output texture through `ImageBuffer.getTexture()`: ```java theme={null} // Process image inputFrame.type = ImageFrame.FrameType.IMAGE; ImageFrame outputFrame = engine.processImage(inputFrame); if (outputFrame == null) { Log.e(TAG, "processImage returned null"); return; } // Get output texture ImageBuffer textureBuffer = outputFrame.getBuffer(); if (textureBuffer == null) { Log.e(TAG, "getBuffer returned null"); return; } // Get output texture ID and dimensions int outputTextureId = textureBuffer.getTexture(); int outputWidth = textureBuffer.getWidth(); int outputHeight = textureBuffer.getHeight(); ``` ### Complete Example ```java theme={null} public class ExternalTextureActivity extends AppCompatActivity implements GLTextureRenderer.OnProcessVideoFrameCallback { private BeautyEffectEngine engine; @Override public int onProcessVideoFrame( GLTextureRenderer.TextureFrame srcFrame, GLTextureRenderer.TextureFrame dstFrame) { // Lazy initialize engine (in OpenGL context) if (engine == null) { BeautyEffectEngine.EngineConfig config = new BeautyEffectEngine.EngineConfig(); config.appId = "your appId"; config.appKey = "your appKey"; config.externalContext = true; // Key: enable external context engine = new BeautyEffectEngine(this, config); engine.setBeautyParam(BasicParam.SMOOTHING, 0.5f); } // Create ImageFrame from input texture int stride = srcFrame.width * 4; ImageFrame inputFrame = ImageFrame.createWithTexture( srcFrame.textureId, srcFrame.width, srcFrame.height, stride); if (inputFrame == null) { return -1; } // Process image ImageFrame outputFrame = engine.processImage( inputFrame, BeautyEffectEngine.ProcessMode.IMAGE); if (outputFrame == null) { return -2; } // Get output texture ImageBuffer textureBuffer = outputFrame.getBuffer(); if (textureBuffer == null) { return -3; } // Set output texture information dstFrame.textureId = textureBuffer.getTexture(); dstFrame.width = textureBuffer.getWidth(); dstFrame.height = textureBuffer.getHeight(); return 0; // Success } } ``` ### Important Notes #### 1. Context Requirements * **Engine must be created in a valid OpenGL context**: When `externalContext = true`, the engine uses the current thread's OpenGL context, so the engine must be created in the OpenGL rendering thread * **Context consistency**: Input texture, engine processing, and output texture must be in the same OpenGL context * **Thread safety**: OpenGL operations must be executed in the same thread #### 2. Texture Format Requirements * **Input texture format**: Supports `GL_RGBA` format `GL_TEXTURE_2D` textures * **Texture parameters**: It is recommended to set the following texture parameters for best results: ```java theme={null} GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); ``` #### 3. Performance Optimization * **Lazy initialization**: Initialize the engine in the first rendering callback to ensure it is created in the correct OpenGL context * **Reuse ImageFrame**: If possible, reuse `ImageFrame` objects to reduce object creation overhead * **Process mode selection**: * `ProcessMode.VIDEO`: Suitable for real-time video stream processing, higher performance * `ProcessMode.IMAGE`: Suitable for single frame image processing, better quality #### 4. Memory Management * **Release resources timely**: Release `ImageFrame` and `ImageBuffer` objects after use * **Texture lifecycle**: Output textures are managed by the engine and do not need manual deletion, but input textures need to be managed by the caller #### 5. Error Handling * **Check return values**: All API calls should check return values * **Null pointer checks**: Check if return values of `createWithTexture()` and `processImage()` are `null` * **Texture validity**: Ensure input texture ID is valid and bound to the current OpenGL context #### 6. Common Issues * **Engine creation failure**: Check if it is created in an OpenGL context and if `externalContext` is correctly set * **Texture processing failure**: Check if texture format is RGBA and texture parameters are correctly set * **Context loss**: If the OpenGL context is destroyed, the engine needs to be recreated ## Lifecycle Management You must call `release()` to release engine resources when Activity/Fragment is destroyed, otherwise it will cause memory leaks. ### Release Resources When Activity or Fragment is destroyed, be sure to release engine resources: ```java theme={null} @Override protected void onDestroy() { super.onDestroy(); if (mBeautyEngine != null) { mBeautyEngine.release(); mBeautyEngine = null; } } ``` ### Memory Management * Release `ImageFrame` and `ImageBuffer` objects timely * Avoid repeatedly creating large numbers of image objects in loops * Recommend reusing `ImageFrame` objects ```java theme={null} // Release resources after use if (inputImage != null) { inputImage.release(); } if (outputImage != null) { outputImage.release(); } if (buffer != null) { buffer.release(); } ``` ## Related Documentation * [Best Practices](/android/best-practices) - Performance optimization and architecture design recommendations * [Common Issues](/android/faq) - Common questions and troubleshooting * [API Reference](/android/api-reference) - Complete API documentation # Quick Start Source: https://facebetter.mintlify.app/android/quick-start Run Android Sample Application ## Environment Requirements * **Android Version**: `Android 5.0` and above * **Development Language**: `Java` * **Architecture Support**: `armeabi-v7a`, `arm64-v8a` ## Get Sample Source Code Clone the [GitHub repository](https://github.com/pixpark/facebetter-sdk) locally, open the project in the `android/demo` directory using Android Studio. ```bash theme={null} git clone https://github.com/pixpark/facebetter-sdk.git ``` ## Configure Application Information and Keys ### Bind Application Package Name Follow the instructions on [this page](/intro/enable-service#bind-application-information) to bind your Android application package name (App Package Name) in the console, for example: `com.example.app` ### Get AppID and AppKey Follow the instructions on [this page](/intro/enable-service#get-appid-and-appkey) to get your `appid` and `appkey`. Open `MainActivity.java` in the project and modify the `appid` and `appkey`. ```java theme={null} BeautyEffectEngine.EngineConfig config = new BeautyEffectEngine.EngineConfig(); config.appId = "your appId"; config.appKey = "your appkey"; // Optional: If licenseJson is provided, license data verification takes priority, appId and appKey are not required // config.licenseJson = "your license json string"; mBeautyEngine = new BeautyEffectEngine(this, config); ``` `licenseJson` takes priority over `appId` + `appKey`. For offline download and details, see [License & Auth](/intro/license). ## Run the Project Sync the project in Android Studio, select a running device, and run it. Android Project # Face Makeup Source: https://facebetter.mintlify.app/effect-creator/face-makeup Create face makeup effects with Facebetter Effect Creator Coming soon. # Face Sticker Source: https://facebetter.mintlify.app/effect-creator/face-sticker Instructions for using the Face Sticker creation plugin ## Workflow Initialization Before designing with the plugin, you need to initialize the workspace: 1. Under the **Workflow Initialization** section, click the **Initialize Workspace** button. 2. The plugin will automatically create a 2048x2048 canvas named `Facebetter_Workspace`. 3. The canvas contains 106 standard face reference points. 4. **Option**: Check "Show Landmark Numbers" to easily identify the numbers of different parts. ## Sticker Layer and Attribute Binding This is the core step of sticker creation, used to define how the sticker follows face movements. 1. **Select Layer**: Select the sticker layer you designed in the Figma canvas (it must be located within the workspace). 2. **Configure Attributes**: * **Blend Mode**: Set the fusion effect between the sticker and the face (e.g., Normal, Overlay, Screen, etc.). * **Position Type**: * **Landmarks**: Follow specific face landmarks (most common). * **Face Rect**: Position based on the entire face area. * **Screen Space**: Fixed at a screen position. * **Trigger Type**: Set the conditions for the sticker to appear (Always show, Face detected, Mouth open, etc.). * **Trigger Delay**: Delay time after triggering. * **Alpha Factor**: Adjust the overall transparency of the sticker. * **FPS**: Set the animation frame rate (if the layer contains multiple frames). ## Preview and Export 1. **3D Preview**: * Click **3D Preview**. * In the popup window, you can see the real-time effect of the sticker on a 3D face model. * Supports left-click to rotate, right-click to pan, and scroll wheel to zoom. 2. **Export Package**: * After confirming the preview, click **Export**. * The package can be directly used in the Facebetter engine and related applications. # Introduction Source: https://facebetter.mintlify.app/effect-creator/introduction Introduction to Facebetter Effect Creator Figma Plugin The Facebetter Effect Creator Figma plugin is a convenient tool specifically designed for creating stickers and makeup effects for the Facebetter SDK. It aims to simplify the creation process for stickers and makeup effects. With this plugin, you can directly complete sticker design, face landmark binding, attribute configuration, and 3D preview export within Figma. ## Login and Language Settings The plugin must be logged in before it can be used. You can log in using a Facebetter account or email, or use social accounts such as Google or GitHub. 1. **Launch Plugin**: Run the Facebetter Effect Creator plugin in Figma. 2. **Login**: Supports Google, GitHub, or email login. 3. **Language Switching**: Click the dropdown menu icon at the top of the interface to switch between **Simplified Chinese** and **English**. Facebetter Effect Creator Plugin Login ## Install Plugin Click this link: [Plugin](https://www.figma.com/community/plugin/1591037386605818409/facebetter-effect-creator), or search for "Facebetter Effect Creator" in Figma, click "Open In" to open the plugin in an existing document or a newly created design document. A new blank document is recommended. Facebetter Effect Creator Plugin Installation ### Attribute Editing Facebetter Effect Creator Plugin Editor ### 3D Preview Facebetter Effect Creator 3D Preview # Screen Sticker Source: https://facebetter.mintlify.app/effect-creator/screen-sticker Create screen stickers with Facebetter Effect Creator Coming soon. # API Reference Source: https://facebetter.mintlify.app/flutter/api-reference Flutter API Reference ## FBEngineConfig | Field | Type | Description | | ----------------- | --------- | ------------------------------------------------------ | | `appId` | `String` | App ID (optional if `licenseJson` provided) | | `appKey` | `String` | App key (optional if `licenseJson` provided) | | `licenseJson` | `String?` | License JSON string (takes priority over appId/appKey) | | `externalContext` | `bool` | Use external OpenGL context (default `false`) | ## FBBeautyEffectEngine ### Static Methods | Method | Description | | ----------------------------------------------- | --------------------------------- | | `Future getSdkVersion()` | Get SDK version | | `Future setLogConfig(FBLogConfig config)` | Set log config (call before init) | | `Future init(FBEngineConfig config)` | Initialize engine | | `FBBeautyEffectEngine get sharedInstance` | Get singleton instance | ### Beauty Parameters | Method | Description | | ----------------------------------------------------------------------------- | ---------------------------------- | | `Future setBasicParam(FBBasicParam param, double value)` | Set basic beauty param \[0.0, 1.0] | | `Future setReshapeParam(FBReshapeParam param, double value)` | Set face reshape param \[0.0, 1.0] | | `Future setMakeupParam(FBMakeupParam param, double value)` | Set makeup param \[0.0, 1.0] | | `Future setLipstickStyle(FBLipstickStyle style)` | Set lipstick style | | `Future setBlushStyle(FBBlushStyle style)` | Set blush style | | `Future setSkinOnlyBeauty(bool enabled)` | Apply beauty to skin only | | `Future setVirtualBackground(FBBackgroundMode mode, {FBImageFrame? bg})` | Set virtual background | ### Filters & Stickers | Method | Description | | ------------------------------------------------------------ | -------------------------------- | | `Future setFilter(String filterId)` | Apply filter | | `Future setFilterIntensity(double intensity)` | Set filter intensity \[0.0, 1.0] | | `Future setSticker(String stickerId)` | Apply sticker (empty to clear) | | `Future registerFilter(String id, String path)` | Register filter from file | | `Future registerFilterData(String id, Uint8List data)` | Register filter from data | | `Future registerSticker(String id, String path)` | Register sticker from file | | `Future registerStickerData(String id, Uint8List data)` | Register sticker from data | | `Future unregisterFilter(String id)` | Unregister filter | | `Future unregisterAllFilters()` | Unregister all filters | | `Future unregisterSticker(String id)` | Unregister sticker | | `Future unregisterAllStickers()` | Unregister all stickers | ### Image Processing | Method | Description | | ----------------------------------------------------------- | ---------------------- | | `Future processImage(FBImageFrame input)` | Process an image frame | | `Future processImageFile(String input, String output)` | Process an image file | ### Callbacks | Property | Type | Description | | ----------------- | ------------------------------- | -------------------- | | `onFaceLandmarks` | `Stream` | Face landmark stream | | `onEngineEvent` | `Stream` | Engine event stream | ### Lifecycle | Method | Description | | ------------------------ | ------------------------ | | `Future release()` | Release engine resources | **Return values:** `0` = success, `-1` = engine not initialized, `null` = processing failed. ## Enumerations ### FBBasicParam | Value | Description | | --------------- | -------------- | | `smoothing(0)` | Skin smoothing | | `sharpening(1)` | Sharpening | | `whitening(2)` | Skin whitening | | `rosiness(3)` | Skin rosiness | ### FBReshapeParam | Value | Description | | --------------- | ----------------------- | | `faceThin(0)` | Face thin | | `faceVShape(1)` | V-shape face | | `eyeSize(8)` | Eye enlargement | | `noseSlim(7)` | Nose slimming | | ... | See full list in source | ### FBImageFormat | Value | Description | | --------- | --------------------------- | | `i420(0)` | YUV 4:2:0 planar | | `nv12(1)` | YUV 4:2:0 semi-planar | | `nv21(2)` | YUV 4:2:0 (Android default) | | `bgra(3)` | BGRA 32bpp | | `rgba(4)` | RGBA 32bpp | | `bgr(5)` | BGR 24bpp | | `rgb(6)` | RGB 24bpp | ## Native Engine Access For streaming SDK integration (TRTC, etc.): **iOS (Swift):** ```swift theme={null} let engine = FacebetterPlugin.sharedInstance().engine ``` **Android (Kotlin):** ```kotlin theme={null} val engine = FacebetterPlugin.sharedInstance?.getEngine() ``` # Implement Beauty Source: https://facebetter.mintlify.app/flutter/implement-beauty Implement beauty effects in Flutter ## Add SDK Dependency Add to your `pubspec.yaml`: ```yaml theme={null} dependencies: facebetter_flutter: ^1.4.4 ``` Then run: ```bash theme={null} flutter pub get ``` For iOS: ```bash theme={null} cd ios && pod install ``` ## Import ```dart theme={null} import 'package:facebetter_flutter/facebetter_flutter.dart'; ``` ## Configure Logging ```dart theme={null} FBBeautyEffectEngine.setLogConfig( FBLogConfig(consoleEnabled: true, level: FBLogLevel.debug), ); ``` Call `setLogConfig` **before** `init()` to see initialization logs. ## Initialize Engine ```dart theme={null} await FBBeautyEffectEngine.init( FBEngineConfig(appId: 'your appId', appKey: 'your appKey'), ); final engine = FBBeautyEffectEngine.sharedInstance; ``` ## Set Beauty Parameters All parameter values range from `[0.0, 1.0]`. Set to `0` to disable. ### Basic Beauty ```dart theme={null} await engine.setBasicParam(FBBasicParam.smoothing, 0.8); await engine.setBasicParam(FBBasicParam.whitening, 0.6); ``` ### Face Reshape ```dart theme={null} await engine.setReshapeParam(FBReshapeParam.faceThin, 0.5); await engine.setReshapeParam(FBReshapeParam.eyeSize, 0.3); ``` ### Makeup ```dart theme={null} await engine.setMakeupParam(FBMakeupParam.lipstick, 0.5); ``` ### Virtual Background ```dart theme={null} await engine.setVirtualBackground(FBBackgroundMode.blur); await engine.setVirtualBackground(FBBackgroundMode.image, backgroundImage: FBImageFrame(...)); ``` ## Filters & Stickers ```dart theme={null} await engine.registerFilter('chuxin', '/path/to/chuxin.fbd'); await engine.setFilter('chuxin'); await engine.setFilterIntensity(0.8); await engine.registerSticker('cherry', '/path/to/cherry.fbd'); await engine.setSticker('cherry'); ``` ## Process Images ```dart theme={null} final inputFrame = FBImageFrame( width: 1080, height: 1920, stride: 4320, data: rgbaBytes, format: FBImageFormat.rgba, ); final outputFrame = await engine.processImage(inputFrame); ``` ## Release ```dart theme={null} await engine.release(); ``` ## Related Docs * [API Reference](/flutter/api-reference) * [TRTC Integration](/flutter/trtc-integration) * [License & Auth](/intro/license) # Quick Start Source: https://facebetter.mintlify.app/flutter/quick-start Run the Flutter example app ## Requirements * **Flutter**: `3.10.0` or later * **Dart**: `3.0.0` or later * **iOS**: `iOS 12.0` or later * **Android**: `Android 5.0` (API 21) or later * **IDE**: Android Studio / VS Code ## Clone the Repository ```bash theme={null} git clone https://github.com/pixpark/facebetter-sdk.git ``` ## Create Flutter Project The demo only contains business code. Create the Flutter project first: ```bash theme={null} cd facebetter-sdk/demo/flutter flutter create --org net.pixpark --project-name fbexample . ``` ## Configure License Follow the [Enable Service](/intro/enable-service) guide to get your `appId` and `appKey`, then update `lib/main.dart`: ```dart theme={null} await FBBeautyEffectEngine.init( FBEngineConfig(appId: 'your appId', appKey: 'your appKey'), ); ``` ## Install Dependencies ```bash theme={null} flutter pub get cd ios && pod install && cd .. ``` ## Run ```bash theme={null} flutter run -d ``` List available devices: ```bash theme={null} flutter devices ``` # TRTC Integration Source: https://facebetter.mintlify.app/flutter/trtc-integration Integrate FaceBetter beauty SDK with TRTC Flutter This guide explains how to integrate FaceBetter beauty SDK with TRTC Flutter. ## Architecture ``` Flutter Layer Native Layer ┌──────────────────┐ ┌─────────────────────────────────┐ │ FBBeautyEffect │ │ TRTC video capture │ │ Engine.init() │ │ ↓ (texture ID) │ │ engine.set*() │ │ onProcessVideoFrame() │ │ set parameters │ │ ↓ │ └──────────────────┘ │ FB engine.processImage(texture) │ │ ↓ │ │ output texture → TRTC streaming │ └─────────────────────────────────┘ ``` ## Prerequisites * TRTC Flutter SDK (`tencent_trtc_cloud`) integrated * `facebetter_flutter` dependency added * Valid FaceBetter license (appId / appKey) ## Step 1: Flutter Layer Initialization Initialize the engine before entering a TRTC room: ```dart theme={null} await FBBeautyEffectEngine.init( FBEngineConfig(appId: 'your_app_id', appKey: 'your_app_key'), ); final engine = FBBeautyEffectEngine.sharedInstance; await engine.setBasicParam(FBBasicParam.smoothing, 0.8); await engine.setBasicParam(FBBasicParam.whitening, 0.6); await engine.setReshapeParam(FBReshapeParam.faceThin, 0.3); ``` ## Step 2: iOS Native Integration ### 2.1 Add Dependency In your iOS `Podfile`: ```ruby theme={null} pod 'TXCustomBeautyProcesserPlugin', '1.0.2' ``` ### 2.2 Implement Beauty Processer Create `FBBeautyProcesser.swift`: ```swift theme={null} import Facebetter import facebetter_flutter class FBBeautyProcesser: NSObject, ITXCustomBeautyProcesser { func getSupportedPixelFormat() -> ITXCustomBeautyPixelFormat { .Texture2D } func getSupportedBufferType() -> ITXCustomBeautyBufferType { .Texture } func onProcessVideoFrame(srcFrame: ITXCustomBeautyVideoFrame, dstFrame: ITXCustomBeautyVideoFrame) -> ITXCustomBeautyVideoFrame { guard let engine = FacebetterPlugin.sharedInstance().engine else { return srcFrame } let inputFrame = FBImageFrame.createWithTexture( srcFrame.texture.textureId, width: srcFrame.width, height: srcFrame.height, stride: srcFrame.width * 4) inputFrame.type = .video guard let outputFrame = engine.processImage(inputFrame) else { return srcFrame } dstFrame.texture.textureId = outputFrame.texture return dstFrame } } class FBBeautyProcesserFactory: NSObject, ITXCustomBeautyProcesserFactory { private var processer: FBBeautyProcesser? func createCustomBeautyProcesser() -> ITXCustomBeautyProcesser { if processer == nil { processer = FBBeautyProcesser() } return processer! } func destroyCustomBeautyProcesser() { processer = nil } } ``` ### 2.3 Register with TRTC In `AppDelegate.swift`: ```swift theme={null} func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { GeneratedPluginRegistrant.register(with: self) TencentTRTCCloud.register(FBBeautyProcesserFactory()) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } ``` Must be called **after** `GeneratedPluginRegistrant.register(with:)`. ### 2.4 Bridging Header Add to your Bridging Header: ```objc theme={null} #import "FacebetterPlugin.h" ``` ## Step 3: Android Native Integration ### 3.1 Add Dependency In `app/build.gradle`: ```groovy theme={null} implementation 'com.tencent.liteav:custom-video-processor:latest.release' ``` ### 3.2 Implement Beauty Processer Create `FBBeautyProcesser.kt`: ```kotlin theme={null} class FBBeautyProcesser : ITXCustomBeautyProcesser { override fun getSupportedPixelFormat() = TXCustomBeautyPixelFormat.Texture2D override fun getSupportedBufferType() = TXCustomBeautyBufferType.Texture override fun onProcessVideoFrame(srcFrame: TXCustomBeautyVideoFrame, dstFrame: TXCustomBeautyVideoFrame) { val engine = FacebetterPlugin.sharedInstance?.getEngine() ?: return val inputFrame = ImageFrame.createWithTexture( srcFrame.texture.textureId, srcFrame.width, srcFrame.height, srcFrame.width * 4) inputFrame.type = ImageFrame.FrameType.VIDEO val outputFrame = engine.processImage(inputFrame) ?: return dstFrame.texture.textureId = outputFrame.texture } } class FBBeautyProcesserFactory : ITXCustomBeautyProcesserFactory { private var processer: FBBeautyProcesser? = null override fun createCustomBeautyProcesser(): ITXCustomBeautyProcesser { if (processer == null) processer = FBBeautyProcesser() return processer!! } override fun destroyCustomBeautyProcesser() { processer = null } } ``` ### 3.3 Register with TRTC In `MainActivity.kt`: ```kotlin theme={null} override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) TXCustomVideoProcessRegisterer.register(FBBeautyProcesserFactory()) } ``` ## Step 4: Enable Custom Video Process ```dart theme={null} await trtcCloud.enableCustomVideoProcess(true); ``` ## Dynamic Parameter Adjustment Adjust beauty parameters during a call: ```dart theme={null} await engine.setBasicParam(FBBasicParam.smoothing, 0.9); await engine.setReshapeParam(FBReshapeParam.faceThin, 0.5); await engine.setFilter('new_filter'); ``` Changes take effect on the next video frame. ## Release ```dart theme={null} await FBBeautyEffectEngine.sharedInstance.release(); ``` ## Notes 1. `onProcessVideoFrame` runs on the GL thread; `processImage` is thread-safe. 2. Call `FBBeautyEffectEngine.init()` before `enableCustomVideoProcess(true)`. 3. The engine is a singleton — only one instance per app lifecycle. 4. FaceBetter uses OpenGL ES 2D textures, matching TRTC's texture format. # Home Source: https://facebetter.mintlify.app/index Realtime AI beauty SDK for Android, iOS, macOS, Windows, Linux, Web, and Flutter. Facebetter is a real-time beauty effects engine for images and live video. Pick your platform below, or start with subscription and licensing. Create a project and get AppID / AppKey. Run the sample, then add beauty to your app. CocoaPods or Framework integration. JavaScript / WebAssembly SDK. Cross-platform plugin and TRTC integration. Build face and screen stickers in Figma. Use the [dashboard](https://facebetter.net/dashboard) for credentials, and the [download page](https://facebetter.net/download) for binaries. # Subscribe Source: https://facebetter.mintlify.app/intro/enable-service Enable Facebetter SDK subscription service and obtain AppID & AppKey to start using beauty features # Enable Subscription Service ## Register/Login After registering and logging in to the dashboard, a default project will be created, displaying current subscription information and statistics. The default project is the free version. Dashboard Subscription Info ## Bind Application Information Facebetter SDK needs to verify the application's identity information during license validation to ensure the authorized application matches the actual application. Please provide the appropriate identifier based on your application platform: * **iOS/macOS**: Bundle ID (e.g., com.example.app) * **Android**: Application Package Name (e.g., com.example.app) * **Windows**: Application Name or Executable File Name * **Linux**: Application Name or Package Name * **Web**: Application Domain (e.g., example.com) * **HarmonyOS**: Application Bundle Name Click the **"Edit Project"** button on the dashboard, select the corresponding identifier type for your application platform, enter the application identifier, and complete the application binding. Console Edit Project ## Subscribe to a Plan Click the **"Subscribe"** button on the "Your Subscription" card in the dashboard to enter the subscription selection page. Follow these steps to complete your subscription: 1. **Select Billing Cycle**: Choose between Pay-as-you-go, Monthly, or Yearly billing (Yearly plans offer a "12 months for the price of 10" discount). 2. **Select Subscription Plan**: Choose the functional version that suits your project needs (e.g., Pro+). 3. **Confirm and Pay**: Verify the information in the order summary on the right, then click "Start Now" to proceed to Stripe for secure payment. Console Subscription Plan ## Get AppID and AppKey In the Dashboard page, on the AppID & AppKey card, you can view your `appid` and `appkey`. Console AppID & AppKey From the same card you can download an **offline license** (`facebetter-offline-license.json`) for offline use. See [License & Auth](./license). # License & Auth Source: https://facebetter.mintlify.app/intro/license Facebetter SDK online auth and offline license guide The SDK supports two license modes. Pick one when creating the engine: | Mode | Config fields | When to use | | ------- | ------------------ | ----------------------------------------- | | Online | `appId` + `appKey` | Device can reach the network | | Offline | `licenseJson` | Offline / weak network / private networks | **Priority**: If `licenseJson` is set, the SDK validates it locally and does **not** call the network. You do not need `appId` / `appKey` in that case. Complete [Subscribe](./enable-service) first: bind your app identifiers in the Dashboard (Bundle ID / package name / domain, etc.) and enable a plan. ## Online auth Copy `AppID` / `AppKey` from the Dashboard and pass them when creating the engine. See each platform’s Quick Start for examples. ## Offline license ### Download 1. Sign in to the [Dashboard](https://facebetter.net/dashboard) 2. Select a project, then on the **AppID & AppKey** card click **Download** for Offline License 3. You get: `facebetter-offline-license.json` Validity matches your current subscription. After renewing or changing the plan, download again and replace the license in your app. ### Integrate Pass the **entire file contents** as a string to `licenseJson`. The same approach works on all platforms. **iOS / macOS** ```objc theme={null} FBEngineConfig *config = [[FBEngineConfig alloc] init]; config.licenseJson = @"/* full file contents */"; self.beautyEffectEngine = [FBBeautyEffectEngine createEngineWithConfig:config]; ``` **Android** ```java theme={null} BeautyEffectEngine.EngineConfig config = new BeautyEffectEngine.EngineConfig(); config.licenseJson = "/* full file contents */"; mBeautyEngine = new BeautyEffectEngine(this, config); ``` **Web** ```js theme={null} const config = new EngineConfig({ licenseJson: '/* full file contents */', }); ``` **Flutter** ```dart theme={null} await FBBeautyEffectEngine.init( FBEngineConfig(licenseJson: '/* full file contents */'), ); ``` Prefer shipping the JSON as an app asset and loading it at runtime instead of hard-coding it in source. ### Notes * The offline license is bound to the app identifiers configured in the Dashboard; a mismatch fails validation. * Not expired: features follow your subscription plan. * Expired: the engine can still initialize, but capabilities fall back to the free plan. Renew and download a new license. * Requires **SDK 1.5.0 or later**. # Overview Source: https://facebetter.mintlify.app/intro/overview Facebetter Beauty Effects SDK Engine Product Overview Facebetter is a real-time beauty effects engine designed for developers. It delivers high-quality AI-powered beauty processing for both images and live video streams. Built on a GPU rendering pipeline, the engine achieves low latency, minimal CPU usage, and high frame rates, and integrates seamlessly into Android, iOS, macOS, Web, and other major platforms. ## Core Features ### Skin Beauty Fine-grained skin enhancement with four independently adjustable parameters — smoothing, whitening, rosy tone, and sharpening. Each parameter supports an intensity range of 0–100 for precise control over the final look. ### Face Reshaping Comprehensive adjustments spanning facial contour and feature proportions: * **Face shape**: face slimming, V-face, narrow face, short face * **Contour**: cheekbone slimming, jaw slimming, chin slimming * **Features**: nose bridge slimming, eye enlarging, eye distance adjustment > A single frame supports up to **5 people** being reshaped simultaneously. ### Makeup Realistic layered makeup effects including lipstick (color and opacity adjustable) and blush (multiple preset shades). Multiple makeup items can be applied at the same time. > A single frame supports up to **5 people** with makeup applied simultaneously. ### Filters & Styles 20+ real-time style filters based on LUT (Look-Up Table) color grading, covering portrait, food, landscape, modern, and other themes. Intensity adjustment and custom LUT extension are supported. ### Virtual Background Powered by real-time portrait segmentation: * **Background blur**: keeps the foreground subject sharp while applying Gaussian blur to the background * **Background replacement**: swaps the background with any image asset ### Sticker Effects 2D sticker effects including: * **Screen stickers**: overlaid at a fixed position on the frame * **Face-tracking stickers**: anchored to face keypoints and dynamically follow head movement Sticker resources are distributed as `.fbpack` packages with support for hot-loading and dynamic switching. ### Advanced Features * **Face keypoint callbacks**: returns 106 facial landmark coordinates per frame for custom effect development and business logic * **Green screen keying (Chroma Key)**: precise removal of green backgrounds, suitable for live streaming and video compositing *** ## Input / Output Formats ### Image Formats | Type | Supported Formats | | ------------- | -------------------------------------------------------- | | Static images | `JPEG`, `PNG`, `BMP` | | Video frames | `YUV I420`, `NV12`, `NV21`, `RGB`, `RGBA`, `BGR`, `BGRA` | | GPU texture | OpenGL / OpenGL ES `Texture` | ### Resolution Limits | Scenario | Maximum | | ----------------------- | -------------------------------- | | Real-time video stream | `720P @ 60fps` / `1080P @ 30fps` | | Static image processing | Up to `4K` | Lossless conversion between all supported formats is available through a unified interface. *** ## Performance | Metric | Value | | ---------------- | --------------------------------------------- | | Render latency | \~`10ms` average at `1080P` (GPU parallel) | | CPU usage | \< `5%` during real-time rendering | | Memory footprint | Primarily GPU memory; system RAM usage is low | *** ## Supported Platforms | Platform | OS / Version | | -------- | ------------------------------------------------------------------------------------- | | Mobile | `iOS 10.0+`, `Android 5.0+`, `HarmonyOS 5.0+` | | Desktop | `macOS 10.13+`, `Windows 10+`, `Linux` | | Web | WebAssembly-based; compatible with Chrome, Firefox, Safari, and other modern browsers | *** ## Integration Languages & Interfaces | Interface | Use Case | | ------------------- | ---------------------------------------------- | | Java / Kotlin | Android applications | | Objective-C / Swift | iOS and macOS applications | | C / C++ | Cross-platform desktop (Windows, Linux, macOS) | | JavaScript (WASM) | Web applications (browser and Node.js) | *** ## Quick Start Select your target platform and follow the integration guide: * [Android Quick Start](../android/quick-start) * [iOS Quick Start](../ios/quick-start) * [macOS Quick Start](../macos/quick-start) * [Web Quick Start](../web/quick-start) # Plans Source: https://facebetter.mintlify.app/intro/pricing Facebetter plan features and pricing details This page describes **feature support** and **pricing** for each Facebetter subscription plan. To purchase or renew, use the [Pricing](https://facebetter.net/pricing) page or the dashboard. ## Pricing One subscription covers all platforms (iOS, Android, macOS, Windows, Linux, Web). Monthly, yearly, and usage-based billing are available; yearly billing is about 20% off. | Plan | Monthly (USD/month) | Yearly (USD/year) | Usage (USD/pack) | Notes | | ----- | ------------------: | ----------------: | ---------------: | ------------------------------------------------------------------------------------ | | Free | \$0 | \$0 | — | 5,000 minutes/month, watermark after 10 min; ideal for development and trials | | Basic | \$100 | \$960 | \$20/pack | Unlimited minutes, no watermark, standard support | | Pro | \$200 | \$1,920 | \$40/pack | Reshape, makeup, style (LUT) filters, and priority support | | Pro+ | \$400 | \$3,840 | \$80/pack | Virtual background, stickers, face keypoint callbacks, green screen keying, and more | > * Usage-based: 10,000 minutes/month per pack, pay as you go, no monthly commitment > * Yearly total ≈ monthly rate × 12 × 0.8; exact amounts are shown on the [Pricing](https://facebetter.net/pricing) page or in the dashboard. ## Feature Support The tables below show which features are included in each plan (✓ = supported, — = not included). ### Usage and Branding | Feature | Free | Basic | Pro | Pro+ | | ------------- | ------------------ | --------- | --------- | --------- | | Monthly usage | 5,000 min/month | Unlimited | Unlimited | Unlimited | | Watermark | Yes (after 10 min) | No | No | No | ### Beauty and Reshape | Feature | Free | Basic | Pro | Pro+ | | --------------------------------------------------------- | ---- | ----- | --- | ---- | | Basic beauty (smoothing, whitening, rosy, sharpening) | ✓ | ✓ | ✓ | ✓ | | Reshape (face slimming, V-face, contour, eyes/nose, etc.) | ✓ | ✓ | ✓ | ✓ | | Makeup (lipstick, blush, etc.) | ✓ | ✓ | ✓ | ✓ | ### Filters and Effects | Feature | Free | Basic | Pro | Pro+ | | ------------------------------------- | ---- | ----- | --- | ---- | | Style (LUT) filters | ✓ | — | ✓ | ✓ | | Stickers | ✓ | — | — | ✓ | | Virtual background (blur/replacement) | ✓ | — | — | ✓ | | Face keypoint callbacks | ✓ | — | — | ✓ | | Green screen keying | ✓ | — | — | ✓ | ### Support | Feature | Free | Basic | Pro | Pro+ | | --------------------------- | ---- | ----- | --- | ---- | | Community support | ✓ | ✓ | ✓ | ✓ | | Email and instant messaging | — | ✓ | ✓ | ✓ | | Priority technical support | — | — | ✓ | ✓ | ## Use Cases | Plan | Use case | | ----- | ------------------------------------------------------------------------------------------------------ | | Free | Development, demos, and small-scale validation; upgrade anytime to remove watermark and increase usage | | Basic | Small to medium teams needing no watermark, unlimited usage, and standard support | | Pro | Teams needing reshape, makeup, LUT filters, and priority support | | Pro+ | Teams needing virtual background, stickers, face keypoints, or green screen keying | To subscribe or change your plan, log in to the [Dashboard](https://facebetter.net/dashboard) and use the subscription section, or go to the [Pricing](https://facebetter.net/pricing) page for current plans and purchase links. # Changelog Source: https://facebetter.mintlify.app/intro/release-note Facebetter SDK Release Note ## v1.5.1 2026-08-03 1. fix: with `SetSkinOnlyBeauty` enabled, processing images of varying resolutions could cause a memory out-of-bounds access (on Web: `memory access out of bounds`; only a full page reload recovered) 2. improvement: inference performance ## v1.5.0 2026-07-30 1. feature: offline license — download from the Dashboard and initialize with `licenseJson` (no network required; all platforms) 2. docs: add [License & Auth](./license) guide for online and offline authentication ## v1.4.7 2026-07-29 1. feature: lipstick and blush style selection APIs 2. improvement: plan renewals / upgrades take effect immediately on the next online auth 3. fix: Web `SetSkinOnlyBeauty` crash 4. fix: rare crashes and black-frame issues during image processing ## v1.4.6 2026-06-22 1. improvement: more stable face tracking when the face moves quickly ## v1.4.5 2026-06-09 1. platform: Flutter SDK for Android and iOS 2. docs: Flutter documentation 3. fix: stickers could show a black screen in external texture mode ## v1.4.4 2026-06-02 1. fix: filters and stickers not applying correctly in external texture mode 2. fix: iOS RGBA texture output in external texture mode ## v1.4.3 2026-05-04 1. feature: lipstick and blush makeup effects 2. feature: additional built-in stickers 3. fix: remove-sticker API not taking effect in some cases ## v1.4.2 2026-04-23 1. fix: Web license validation edge cases ## v1.4.1 2026-04-12 1. platform: Windows SDK includes both 32-bit and 64-bit libraries ## v1.4.0 2026-03-24 1. feature: skin segmentation — new `SetSkinOnlyBeauty` API to apply beauty only on skin areas ## v1.3.2 2026-03-10 1. improvement: free version displays watermark after 10 minutes 2. platform: added Windows SDK and Linux SDK support ## v1.3.1 2026-03-03 1. web: web npm version upgraded to 1.3.0 2. improvement: added mirror interface documentation 3. deprecation: deprecated enable beauty type interface ## v1.3.0 2026-02-20 1. improvement: optimized processing performance 2. platform: Android supports Maven repository dependency 3. platform: iOS and macOS Demo support CocoaPods installation 4. feature: support style filters and stickers 5. docs: updated filter and sticker development documentation ## v1.2.5 2026-01-15 1. improvement: optimized authentication and reporting logic 2. platform: support WebAssembly authentication and reporting ## v1.2.2 2026-01-08 1. feature: free version limited to 5000 calls per month 2. feature: support pay-as-you-go billing ## v1.2.1 2025-12-20 1. feature: added watermark filter 2. improvement: network verification changed to async + callback notification 3. price: price plan adjustment, added Ultra tier ## v1.2.0 2025-12-01 1. platform: full platform support (Android, iOS, macOS, Windows, Linux, Web) 2. feature: virtual background 3. feature: style filters 4. feature: sticker effects 5. feature: online authentication upgrade 6. improvement: optimized keypoint stability ## v1.1.3 2025-11-10 1. feature: free version limited to 5000 calls per month 2. feature: pay-as-you-go billing ## v1.1.2 2025-10-28 1. improvement: optimized keypoint stability 2. fix: fixed keypoint jitter issue ## v1.1.1 2025-10-15 1. feature: LUT filter 2. feature: face keypoint callback 3. feature: green screen matting 4. feature: support binding app ID 5. feature: online authentication v2 6. feature: multi-project support ## v1.1.0 2025-09-20 1. feature: support external texture processing 2. improvement: optimized API usability ## v1.0.6 2025-09-05 1. improvement: optimized Android, iOS and macOS Demo 2. fix: fixed Android layout display issue 3. platform: added Android camera permission check ## v1.0.5 2025-08-18 1. improvement: optimized beauty parameter adjustment ## v1.0.4 2025-08-01 1. improvement: optimized face detection accuracy 2. improvement: resource file size optimization ## v1.0.3 2025-07-15 1. fix: fixed makeup only applying to one face when multiple faces detected ## v1.0.2 2025-07-01 1. feature: virtual background support 2. web: Web Chinese and English documentation 3. web: npm installation support # API Reference Source: https://facebetter.mintlify.app/ios/api-reference iOS API Reference ## Logging Related ### Log Levels Log level enumeration for controlling log output levels. ```objc theme={null} typedef NS_ENUM(NSInteger, FBLogLevel) { FBLogLevel_Trace = 0, FBLogLevel_Debug, FBLogLevel_Info, FBLogLevel_Warn, FBLogLevel_Error, FBLogLevel_Critical, }; ``` ### Log Configuration Class Log configuration class for configuring log output methods and levels. **Field Descriptions:** * `consoleEnabled`: Whether to enable console output * `fileEnabled`: Whether to enable file output * `level`: Log level * `fileName`: Log file path (only effective when `fileEnabled` is `YES`) ```objc theme={null} FB_OBJC_API @interface FBLogConfig : NSObject @property(nonatomic, assign) BOOL consoleEnabled; @property(nonatomic, assign) BOOL fileEnabled; @property(nonatomic, assign) FBLogLevel level; @property(nonatomic, copy, nullable) NSString* fileName; - (instancetype)init; @end ``` ## Engine Related ### Process Mode Image processing mode enumeration. * `FBProcessModeImage`: Image mode, suitable for single image processing * `FBProcessModeVideo`: Video mode, suitable for video streams and live streaming scenarios, better performance ```objc theme={null} typedef NS_ENUM(NSInteger, FBProcessMode) { FBProcessModeImage = 0, // Image mode FBProcessModeVideo = 1 // Video mode }; ``` ### Engine Configuration Engine configuration class for initializing the beauty engine. **Field Descriptions:** * `appId`: Application ID (optional, not required if `licenseJson` is provided) * `appKey`: Application key (optional, not required if `licenseJson` is provided) * `licenseJson`: License data JSON string (optional, if provided, takes priority and `appId` and `appKey` are not required) * `externalContext`: Whether to use external OpenGL context (default `NO`) * `YES`: Use the caller-provided GL context, SDK will not create/manage internal context * `NO`: Use internal default context **Verification Priority:** * If `licenseJson` is not empty, use license data verification (supports online response and offline license) * Otherwise, use `appId` and `appKey` for automatic online verification ```objc theme={null} FB_OBJC_API @interface FBEngineConfig : NSObject @property(nonatomic, copy) NSString* appId; @property(nonatomic, copy) NSString* appKey; @property(nonatomic, copy, nullable) NSString* licenseJson; // License data JSON string (optional) @property(nonatomic, assign) BOOL externalContext; // Whether to use external OpenGL context - (instancetype)init; @end ``` ### Engine Interface Main beauty effect engine class providing entry point for beauty functionality. **Static Methods:** * `setLogConfig:`: Set log configuration **Instance Methods:** #### Parameter Settings * `setBasicParam:floatValue:`: Set basic beauty parameters (range 0.0 - 1.0) * `setReshapeParam:floatValue:`: Set face reshape parameters (range 0.0 - 1.0) * `setMakeupParam:floatValue:`: Set makeup parameters (range 0.0 - 1.0) * `setLipstickStyle:`: Set lipstick style (Rouge / Coral / Pink) * `setBlushStyle:`: Set blush style (Classic / Peach / Rose) * `setSkinOnlyBeauty:`: Set whether beauty is applied only to skin regions * Parameter: `enabled` - `YES` to enable skin-only beauty, `NO` to apply to entire image * `setVirtualBackground:`: Set virtual background * Parameter: `FBVirtualBackgroundOptions` object containing background mode and background image #### Filters & Stickers Management * `setFilter:`: Set filter * Parameter: `filterId` unique filter identifier * `setFilterIntensity:`: Set filter intensity * Parameter: `intensity` intensity value (range 0.0 - 1.0) * `setSticker:`: Set sticker * Parameter: `stickerId` unique sticker identifier, pass @"" to clear the sticker * `registerFilter:fbdFilePath:`: Register filter from file * `registerFilter:fbdData:`: Register filter from memory * `registerSticker:fbdFilePath:`: Register sticker from file * `registerSticker:fbdData:`: Register sticker from memory * `unregisterFilter:`: Unload filter * `unregisterAllFilters`: Unload all filters * `unregisterSticker:`: Unload sticker * `unregisterAllStickers`: Unload all stickers * `getRegisteredFilters`: Get list of registered filters * `getRegisteredStickers`: Get list of registered stickers #### Image Processing * `processImage:`: Process image frame * Frame type is obtained from `imageFrame.type` property (`FBFrameTypeImage` or `FBFrameTypeVideo`) * The processed image frame will maintain the same frame type **Return Value Descriptions:** * Methods return `int` type: `0` indicates success, other values indicate error codes * `processImage:processMode:` returns `FBImageFrame`: Processed image frame, returns `nil` on failure ```objc theme={null} FB_OBJC_API @interface FBBeautyEffectEngine : NSObject + (int)setLogConfig:(FBLogConfig*)config; + (instancetype)createEngineWithConfig:(FBEngineConfig*)config; - (int)setBasicParam:(FBBasicParam)param floatValue:(float)value; - (int)setReshapeParam:(FBReshapeParam)param floatValue:(float)value; - (int)setMakeupParam:(FBMakeupParam)param floatValue:(float)value; - (int)setLipstickStyle:(FBLipstickStyle)style; - (int)setBlushStyle:(FBBlushStyle)style; - (int)setSkinOnlyBeauty:(BOOL)enabled; - (int)setVirtualBackground:(FBVirtualBackgroundOptions*)options; - (int)setFilter:(NSString*)filterId; - (int)setFilterIntensity:(float)intensity; - (int)setSticker:(NSString*)stickerId; - (int)registerFilter:(NSString*)filterId fbdFilePath:(NSString*)fbdFilePath; - (int)registerFilter:(NSString*)filterId fbdData:(NSData*)fbdData; - (int)registerSticker:(NSString*)stickerId fbdFilePath:(NSString*)fbdFilePath; - (int)registerSticker:(NSString*)stickerId fbdData:(NSData*)fbdData; - (int)unregisterFilter:(NSString*)filterId; - (int)unregisterAllFilters; - (int)unregisterSticker:(NSString*)stickerId; - (int)unregisterAllStickers; - (NSArray*)getRegisteredFilters; - (NSArray*)getRegisteredStickers; - (FBImageFrame* _Nullable)processImage:(FBImageFrame*)imageFrame; // Deprecated APIs - (int)setBeautyTypeEnabled:(FBBeautyType)type enabled:(BOOL)enabled; - (BOOL)isBeautyTypeEnabled:(FBBeautyType)type; - (int)disableAllBeautyTypes; @end ``` ## Beauty Parameters Beauty parameter enumeration classes containing all beauty-related parameter type definitions. ### Beauty Types Define available beauty functionality types. ```objc theme={null} typedef NS_ENUM(NSInteger, FBBeautyType) { FBBeautyType_Basic = 0, // Basic beauty FBBeautyType_Reshape, // Face reshape FBBeautyType_Makeup, // Makeup effects FBBeautyType_VirtualBackground, // Virtual background }; ``` ### Basic Beauty Parameters Basic beauty effect parameter types, all parameter value ranges are `0.0 - 1.0`. ```objc theme={null} typedef NS_ENUM(NSInteger, FBBasicParam) { FBBasicParam_Smoothing = 0, // Smoothing FBBasicParam_Sharpening, // Sharpening FBBasicParam_Whitening, // Whitening FBBasicParam_Rosiness, // Rosiness }; ``` ### Face Reshape Parameters Face reshape effect parameter types, all parameter value ranges are `0.0 - 1.0`. ```objc theme={null} typedef NS_ENUM(NSInteger, FBReshapeParam) { FBReshapeParam_FaceThin = 0, // Face thinning FBReshapeParam_FaceVShape, // V-shaped face FBReshapeParam_FaceNarrow, // Narrow face FBReshapeParam_FaceShort, // Short face FBReshapeParam_Cheekbone, // Cheekbone FBReshapeParam_Jawbone, // Jawbone FBReshapeParam_Chin, // Chin FBReshapeParam_NoseSlim, // Nose slimming FBReshapeParam_EyeSize, // Eye enlargement FBReshapeParam_EyeDistance, // Eye distance }; ``` ### Makeup Parameters Makeup effect parameter types, all parameter value ranges are `0.0 - 1.0`. ```objc theme={null} typedef NS_ENUM(NSInteger, FBMakeupParam) { FBMakeupParam_Lipstick = 0, // Lipstick FBMakeupParam_Blush, // Blush }; typedef NS_ENUM(NSInteger, FBLipstickStyle) { FBLipstickStyle_Rouge = 0, // Rose red FBLipstickStyle_Coral, // Coral FBLipstickStyle_Pink, // Pink (default) }; typedef NS_ENUM(NSInteger, FBBlushStyle) { FBBlushStyle_Classic = 0, // Classic (default) FBBlushStyle_Peach, // Peach FBBlushStyle_Rose, // Rose }; ``` ### Virtual Background Options Virtual background effect configuration options. ```objc theme={null} FB_OBJC_API @interface FBVirtualBackgroundOptions : NSObject @property(nonatomic, assign) FBBackgroundMode mode; @property(nonatomic, strong, nullable) FBImageFrame *backgroundImage; - (instancetype)init; - (instancetype)initWithMode:(FBBackgroundMode)mode; @end;; ``` ## Image Related ### FBImageFrame Image frame class for encapsulating image data and processing. **Creation Methods:** * `createWithFile:`: Create from file (supports PNG, JPG) * `createWithRGBA:width:height:stride:`: Create from RGBA data * `createWithBGRA:width:height:stride:`: Create from BGRA data * `createWithRGB:width:height:stride:`: Create from RGB data * `createWithBGR:width:height:stride:`: Create from BGR data * `createWithI420:...`: Create from I420 YUV data * `createWithNV12:...`: Create from NV12 YUV data * `createWithNV21:...`: Create from NV21 YUV data * `createWithTexture:width:height:stride:`: Create from GPU texture (external texture input) * `createWithUIImage:`: Create from UIImage (iOS only) **Image Operations:** * `rotate:`: Rotate image (returns 0 for success) * `mirror:`: Mirror image (returns 0 for success) * Parameter: `mode` mirror mode, can be "horizontal", "vertical", or "both" (case insensitive) * `setMirror:`: Set mirror mode for engine processing (avoids extra format conversion) * Parameter: `mode` mirror mode, can be "horizontal", "vertical", or "both" (case insensitive) * Note: This only sets the mirror flag, actual mirroring happens during engine processing **Format Conversion:** * `convert:`: Format conversion method, returns converted `FBImageFrame` * `toFile:quality:`: Save image to file (specify quality 0-100) * `toFile:`: Save image to file (use default quality 90) ```objc theme={null} FB_OBJC_API @interface FBImageFrame : NSObject + (FBImageFrame *_Nullable)createWithFile:(NSString *)filePath; + (FBImageFrame *_Nullable)createWithRGBA:(const uint8_t *)data width:(int)width height:(int)height stride:(int)stride; + (FBImageFrame *_Nullable)createWithBGRA:(const uint8_t *)data width:(int)width height:(int)height stride:(int)stride; + (FBImageFrame *_Nullable)createWithRGB:(const uint8_t *)data width:(int)width height:(int)height stride:(int)stride; + (FBImageFrame *_Nullable)createWithBGR:(const uint8_t *)data width:(int)width height:(int)height stride:(int)stride; + (FBImageFrame *_Nullable)createWithI420:(int)width height:(int)height dataY:(const uint8_t *)dataY strideY:(int)strideY dataU:(const uint8_t *)dataU strideU:(int)strideU dataV:(const uint8_t *)dataV strideV:(int)strideV; + (FBImageFrame *_Nullable)createWithNV12:(int)width height:(int)height dataY:(const uint8_t *)dataY strideY:(int)strideY dataUV:(const uint8_t *)dataUV strideUV:(int)strideUV; + (FBImageFrame *_Nullable)createWithNV21:(int)width height:(int)height dataY:(const uint8_t *)dataY strideY:(int)strideY dataUV:(const uint8_t *)dataUV strideUV:(int)strideUV; + (FBImageFrame *_Nullable)createWithTexture:(uint32_t)texture width:(int)width height:(int)height stride:(int)stride; + (FBImageFrame *_Nullable)createWithUIImage:(UIImage *)image; - (int)rotate:(FBImageRotation)rotation; - (int)mirror:(NSString *)mode; - (void)setMirror:(NSString *)mode; - (FBImageFrame *_Nullable)convert:(FBImageFormat)format; - (int)toFile:(NSString *)filePath quality:(int)quality; - (int)toFile:(NSString *)filePath; // Property access methods - (int32_t)width; - (int32_t)height; - (int32_t)stride; - (int32_t)size; - (const uint8_t *_Nullable)data; - (FBImageFormat)format; - (uint32_t)texture; @property(nonatomic, assign) FBFrameType type; // YUV related methods - (const uint8_t *_Nullable)dataY; - (const uint8_t *_Nullable)dataU; - (const uint8_t *_Nullable)dataV; - (const uint8_t *_Nullable)dataUV; - (int32_t)strideY; - (int32_t)strideU; - (int32_t)strideV; - (int32_t)strideUV; @end ``` ### Enum Types **Supported Image Formats:** * `FBImageFormatI420`: YUV 4:2:0 (3 planes, Y, U, V) * `FBImageFormatNV12`: YUV 4:2:0 (2 planes, Y + UV) * `FBImageFormatNV21`: YUV 4:2:0 (2 planes, Y + VU) * `FBImageFormatRGBA`, `FBImageFormatBGRA`: 32-bit RGBA/BGRA * `FBImageFormatRGB`, `FBImageFormatBGR`: 24-bit RGB/BGR * `FBImageFormatTexture`: Texture format **Image Rotation Angles:** * `FBImageRotation0`: 0 degrees * `FBImageRotation90`: Clockwise 90 degrees * `FBImageRotation180`: Clockwise 180 degrees * `FBImageRotation270`: Clockwise 270 degrees **Property Access:** * `width`, `height`: Get image width and height * `size`: Get image data size (in bytes) * `stride`: Get image stride **Data Access:** * `data`: Get raw image data pointer * YUV format specific: `dataY`, `dataU`, `dataV`, `dataUV` * YUV format strides: `strideY`, `strideU`, `strideV`, `strideUV` ```objc theme={null} typedef NS_ENUM(NSInteger, FBImageFormat) { FBImageFormatI420, FBImageFormatNV12, FBImageFormatNV21, FBImageFormatBGRA, FBImageFormatRGBA, FBImageFormatBGR, FBImageFormatRGB, FBImageFormatTexture, }; typedef NS_ENUM(NSInteger, FBImageRotation) { FBImageRotation0, // 0 degrees FBImageRotation90, // Clockwise 90 degrees FBImageRotation180, // Clockwise 180 degrees FBImageRotation270, // Clockwise 270 degrees }; typedef NS_ENUM(NSInteger, FBFrameType) { FBFrameTypeImage = 0, // Image mode FBFrameTypeVideo = 1 // Video mode }; ``` ## Deprecated APIs **Deprecated** The following APIs are deprecated. ### Beauty Type Control * `setBeautyTypeEnabled:enabled:` * **Description**: \[Deprecated] Enable or disable beauty type (No effect in parameter-driven mode) * **Return Value**: `0` indicates success * `isBeautyTypeEnabled:` * **Description**: \[Deprecated] Check if beauty type is enabled (Always returns NO) * **Return Value**: `NO` * `disableAllBeautyTypes` * **Description**: \[Deprecated] Disable all beauty types (Please reset effects by zeroing parameters) * **Return Value**: `0` indicates success # Best Practices Source: https://facebetter.mintlify.app/ios/best-practices iOS Beauty SDK Best Practices Guide ## Performance Optimization ### 1. Choose the Right Processing Mode **Video Mode (FBProcessModeVideo)** * Suitable for real-time video streams and live streaming scenarios * Better performance and faster processing speed * Recommended for camera preview, video calls, and similar scenarios **Image Mode (FBProcessModeImage)** * Suitable for single image processing * Higher quality and better effects * Recommended for photo editing and image beautification scenarios ```objc theme={null} // Real-time video processing input.type = FBFrameTypeVideo; FBImageFrame *output = [engine processImage:input]; // High-quality image processing input.type = FBFrameTypeImage; FBImageFrame *output = [engine processImage:input]; ``` ### 2. Parameter Adjustment Recommendations **Beauty Parameter Adjustment Principles** * Start with smaller values to avoid over-beautification * Recommend reducing parameter values in real-time scenarios for smooth performance * Static images can have appropriately higher parameter values * Adjust parameter ranges based on user groups and scenarios **Recommended Parameter Ranges** ```objc theme={null} // Basic beauty parameters (real-time scenarios) [self.beautyEngine setBasicParam:FBBasicParam_Smoothing floatValue:0.2f]; // Smoothing [self.beautyEngine setBasicParam:FBBasicParam_Whitening floatValue:0.1f]; // Whitening [self.beautyEngine setBasicParam:FBBasicParam_Rosiness floatValue:0.1f]; // Rosiness // Face reshaping parameters (real-time scenarios) [self.beautyEngine setReshapeParam:FBReshapeParam_FaceThin floatValue:0.1f]; // Face thinning [self.beautyEngine setReshapeParam:FBReshapeParam_EyeSize floatValue:0.1f]; // Eye enlargement ``` ### 3. Memory Optimization **Use ARC for Memory Management** ```objc theme={null} // Recommended: Use ARC automatic management @property (nonatomic, strong) FBImageFrame *reusableFrame; // Avoid: Manual memory management (unless necessary) // FBImageFrame *frame = [[FBImageFrame alloc] init]; // [frame release]; ``` **Release Resources Timely** ```objc theme={null} @interface BeautyProcessor : NSObject @property (nonatomic, strong) FBImageFrame *reusableFrame; // Reusable object @end @implementation BeautyProcessor - (FBImageFrame *)processImage:(NSData *)imageData { // Reuse FBImageFrame object if (self.reusableFrame == nil) { self.reusableFrame = [FBImageFrame createWithRGBA:data width:width height:height stride:stride]; } // Process image self.reusableFrame.type = FBFrameTypeVideo; FBImageFrame *output = [self.beautyEngine processImage:self.reusableFrame]; return output; } - (void)release { self.reusableFrame = nil; // ARC will automatically release } @end ``` ## Architecture Design ### 1. Singleton Pattern for Engine Management ```objc theme={null} @interface BeautyEngineManager : NSObject @property (nonatomic, strong, readonly) FBBeautyEffectEngine *engine; + (instancetype)sharedManager; - (void)releaseEngine; @end @implementation BeautyEngineManager + (instancetype)sharedManager { static BeautyEngineManager *instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[BeautyEngineManager alloc] init]; }); return instance; } - (instancetype)init { self = [super init]; if (self) { [self initEngine]; } return self; } - (void)initEngine { FBEngineConfig *config = [[FBEngineConfig alloc] init]; config.appId = @"your_app_id"; config.appKey = @"your_app_key"; _engine = [FBBeautyEffectEngine createEngineWithConfig:config]; } - (void)releaseEngine { _engine = nil; } @end ``` ### 2. Asynchronous Image Processing ```objc theme={null} @interface AsyncBeautyProcessor : NSObject @property (nonatomic, strong) dispatch_queue_t processingQueue; @property (nonatomic, strong) FBBeautyEffectEngine *engine; @end @implementation AsyncBeautyProcessor - (instancetype)init { self = [super init]; if (self) { self.processingQueue = dispatch_queue_create("com.facebetter.processing", DISPATCH_QUEUE_SERIAL); self.engine = [BeautyEngineManager sharedManager].engine; } return self; } - (void)processImageAsync:(FBImageFrame *)input completion:(void(^)(FBImageFrame *result, NSError *error))completion { dispatch_async(self.processingQueue, ^{ @try { input.type = FBFrameTypeVideo; FBImageFrame *output = [self.engine processImage:input]; // Switch to main thread for callback dispatch_async(dispatch_get_main_queue(), ^{ completion(output, nil); }); } @catch (NSException *exception) { NSError *error = [NSError errorWithDomain:@"FacebetterError" code:-1 userInfo:@{NSLocalizedDescriptionKey: exception.reason}]; dispatch_async(dispatch_get_main_queue(), ^{ completion(nil, error); }); } }); } @end ``` ## Error Handling ### 1. Comprehensive Error Handling Mechanism ```objc theme={null} @interface RobustBeautyProcessor : NSObject @property (nonatomic, strong) FBBeautyEffectEngine *engine; @end @implementation RobustBeautyProcessor - (BOOL)processImage:(FBImageFrame *)input output:(FBImageFrame *)output { // Parameter validation if (self.engine == nil) { NSLog(@"Engine not initialized"); return NO; } if (input == nil) { NSLog(@"Invalid input image"); return NO; } @try { // Process image FBImageFrame *result = [self.engine processImage:input processMode:FBProcessModeVideo]; if (result == nil) { NSLog(@"Failed to process image"); return NO; } // Copy result to output [self copyImageFrame:result to:output]; return YES; } @catch (NSException *exception) { NSLog(@"Exception during processing: %@", exception.reason); return NO; } } - (void)copyImageFrame:(FBImageFrame *)src to:(FBImageFrame *)dst { // Implement image copying logic } @end ``` ### 2. Retry Mechanism ```objc theme={null} @interface RetryBeautyProcessor : NSObject @property (nonatomic, strong) FBBeautyEffectEngine *engine; @end @implementation RetryBeautyProcessor static const int MAX_RETRY_COUNT = 3; static const NSTimeInterval RETRY_DELAY = 0.1; - (FBImageFrame *)processImageWithRetry:(FBImageFrame *)input { for (int i = 0; i < MAX_RETRY_COUNT; i++) { @try { FBImageFrame *result = [self.engine processImage:input processMode:FBProcessModeVideo]; if (result != nil) { return result; } } @catch (NSException *exception) { NSLog(@"Attempt %d failed: %@", i + 1, exception.reason); } if (i < MAX_RETRY_COUNT - 1) { [NSThread sleepForTimeInterval:RETRY_DELAY]; } } NSLog(@"All retry attempts failed"); return nil; } @end ``` ## Lifecycle Management The beauty engine is a singleton and is automatically released when the app ends. Manual management is not required. However, stopping beauty processing when leaving a page/ViewController can save resources. ### 1. ViewController Lifecycle Handling ```objc theme={null} @interface BeautyViewController : UIViewController @property (nonatomic, strong) BeautyEngineManager *beautyManager; @end @implementation BeautyViewController - (void)viewDidLoad { [super viewDidLoad]; // Initialize beauty engine self.beautyManager = [BeautyEngineManager sharedManager]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // Resume beauty processing } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // Pause beauty processing } - (void)dealloc { // Release beauty engine (if no longer needed) // [self.beautyManager releaseEngine]; } @end ``` ### 2. Application Lifecycle Handling ```objc theme={null} - (void)applicationDidEnterBackground:(UIApplication *)application { // Pause beauty processing when app enters background [[BeautyEngineManager sharedManager] pauseProcessing]; } - (void)applicationWillEnterForeground:(UIApplication *)application { // Resume beauty processing when app returns to foreground [[BeautyEngineManager sharedManager] resumeProcessing]; } ``` ## Performance Monitoring ### 1. Performance Metrics Monitoring ```objc theme={null} @interface BeautyPerformanceMonitor : NSObject @property (nonatomic, assign) NSTimeInterval startTime; @property (nonatomic, assign) NSInteger frameCount; @property (nonatomic, assign) NSTimeInterval totalProcessTime; @end @implementation BeautyPerformanceMonitor - (void)startFrame { self.startTime = [[NSDate date] timeIntervalSince1970]; } - (void)endFrame { NSTimeInterval processTime = [[NSDate date] timeIntervalSince1970] - self.startTime; self.totalProcessTime += processTime; self.frameCount++; // Calculate average processing time if (self.frameCount % 30 == 0) { // Calculate every 30 frames NSTimeInterval avgTime = self.totalProcessTime / self.frameCount; NSLog(@"Average process time: %.2fms", avgTime * 1000); // Reset counters self.totalProcessTime = 0; self.frameCount = 0; } } - (BOOL)isPerformanceGood { return (self.totalProcessTime / MAX(self.frameCount, 1)) < 0.033; // 30fps } @end ``` ### 2. Memory Usage Monitoring ```objc theme={null} @interface MemoryMonitor : NSObject @end @implementation MemoryMonitor - (void)logMemoryUsage:(NSString *)tag { struct mach_task_basic_info info; mach_msg_type_number_t size = MACH_TASK_BASIC_INFO_COUNT; kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size); if (kerr == KERN_SUCCESS) { NSLog(@"%@ - Used: %.2fMB, Resident: %.2fMB", tag, info.resident_size / 1024.0 / 1024.0, info.resident_size / 1024.0 / 1024.0); } } - (BOOL)isMemoryLow { struct mach_task_basic_info info; mach_msg_type_number_t size = MACH_TASK_BASIC_INFO_COUNT; kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size); if (kerr == KERN_SUCCESS) { // Check if memory usage is too high (adjust threshold based on actual situation) return info.resident_size > 100 * 1024 * 1024; // 100MB } return NO; } @end ``` ## Configuration Management ### 1. Beauty Configuration Management ```objc theme={null} @interface BeautyConfigManager : NSObject @property (nonatomic, strong) NSUserDefaults *userDefaults; @end @implementation BeautyConfigManager - (instancetype)init { self = [super init]; if (self) { self.userDefaults = [NSUserDefaults standardUserDefaults]; } return self; } - (void)saveBeautyConfig:(BeautyConfig *)config { NSDictionary *configDict = @{ @"smoothing": @(config.smoothing), @"whitening": @(config.whitening), @"face_thin": @(config.faceThin), @"basic_enabled": @(config.basicEnabled), @"reshape_enabled": @(config.reshapeEnabled) }; [self.userDefaults setObject:configDict forKey:@"beauty_config"]; [self.userDefaults synchronize]; } - (BeautyConfig *)loadBeautyConfig { BeautyConfig *config = [[BeautyConfig alloc] init]; NSDictionary *configDict = [self.userDefaults objectForKey:@"beauty_config"]; if (configDict) { config.smoothing = [configDict[@"smoothing"] floatValue]; config.whitening = [configDict[@"whitening"] floatValue]; config.faceThin = [configDict[@"face_thin"] floatValue]; config.basicEnabled = [configDict[@"basic_enabled"] boolValue]; config.reshapeEnabled = [configDict[@"reshape_enabled"] boolValue]; } return config; } @end @interface BeautyConfig : NSObject @property (nonatomic, assign) float smoothing; @property (nonatomic, assign) float whitening; @property (nonatomic, assign) float faceThin; @property (nonatomic, assign) BOOL basicEnabled; @property (nonatomic, assign) BOOL reshapeEnabled; @end @implementation BeautyConfig - (instancetype)init { self = [super init]; if (self) { self.smoothing = 0.3f; self.whitening = 0.2f; self.faceThin = 0.1f; self.basicEnabled = YES; self.reshapeEnabled = NO; } return self; } @end ``` ## Testing Recommendations ### 1. Unit Testing ```objc theme={null} @interface BeautyEffectTests : XCTestCase @property (nonatomic, strong) FBBeautyEffectEngine *beautyEngine; @end @implementation BeautyEffectTests - (void)setUp { [super setUp]; FBEngineConfig *config = [[FBEngineConfig alloc] init]; config.appId = @"test_app_id"; config.appKey = @"test_app_key"; self.beautyEngine = [FBBeautyEffectEngine createEngineWithConfig:config]; } - (void)testBeautyEngineInitialization { XCTAssertNotNil(self.beautyEngine, @"Engine should be created"); } - (void)testImageProcessing { // Create test image uint8_t *data = malloc(640 * 480 * 4); FBImageFrame *input = [FBImageFrame createWithRGBA:data width:640 height:480 stride:640 * 4]; // Process image FBImageFrame *output = [self.beautyEngine processImage:input processMode:FBProcessModeVideo]; XCTAssertNotNil(output, @"Output should not be null"); free(data); } - (void)tearDown { self.beautyEngine = nil; [super tearDown]; } @end ``` ### 2. Performance Testing ```objc theme={null} - (void)testPerformance { [self measureBlock:^{ NSTimeInterval startTime = [[NSDate date] timeIntervalSince1970]; for (int i = 0; i < 100; i++) { // Process test image [self processTestImage]; } NSTimeInterval endTime = [[NSDate date] timeIntervalSince1970]; NSTimeInterval totalTime = endTime - startTime; NSTimeInterval avgTime = totalTime / 100; XCTAssertLessThan(avgTime, 0.05, @"Average process time should be less than 50ms"); }]; } ``` # Error Handling Source: https://facebetter.mintlify.app/ios/error-handling iOS Beauty SDK Error Handling Guide ## Error Code Handling ### 1. Check API Return Values All API calls should check return values: ```objc theme={null} // Check engine creation FBEngineConfig *config = [[FBEngineConfig alloc] init]; config.appId = @"your_app_id"; config.appKey = @"your_app_key"; self.engine = [FBBeautyEffectEngine createEngineWithConfig:config]; if (self.engine == nil) { NSLog(@"Failed to create beauty engine"); return; } // Check parameter setting int ret = [self.engine setBasicParam:FBBeautyParam_Smoothing floatValue:0.5f]; if (ret == 0) { NSLog(@"Successfully set basic beauty parameters"); } else { NSLog(@"Failed to set smoothing param, error code: %d", ret); } ``` ### 2. Common Error Codes According to API documentation, main error codes: * **0**: Success * **-1**: Engine not initialized ```objc theme={null} @interface BeautyErrorHandler : NSObject @end @implementation BeautyErrorHandler + (void)handleError:(int)errorCode operation:(NSString *)operation { switch (errorCode) { case 0: NSLog(@"%@ succeeded", operation); break; case -1: NSLog(@"%@ failed: Engine not initialized", operation); break; default: NSLog(@"%@ failed: Unknown error code %d", operation, errorCode); break; } } @end ``` ## Handle Image Data ### 1. Input Image Validation ```objc theme={null} @interface ImageValidator : NSObject + (BOOL)validateImageFrame:(FBImageFrame *)imageFrame; @end @implementation ImageValidator + (BOOL)validateImageFrame:(FBImageFrame *)imageFrame { if (imageFrame == nil) { NSLog(@"ImageFrame is nil"); return NO; } // Check image dimensions int width = imageFrame.width; int height = imageFrame.height; if (width <= 0 || height <= 0) { NSLog(@"Invalid image dimensions: %dx%d", width, height); return NO; } return YES; } @end ``` ### 2. Image Processing Error Handling ```objc theme={null} @interface ImageProcessor : NSObject @property (nonatomic, strong) FBBeautyEffectEngine *engine; @end @implementation ImageProcessor - (FBImageFrame *)processImageSafely:(FBImageFrame *)input { // Validate input if (![ImageValidator validateImageFrame:input]) { return nil; } @try { // Process image input.type = FBFrameTypeVideo; FBImageFrame *output = [self.engine processImage:input]; if (output == nil) { NSLog(@"Failed to process image: output is nil"); return nil; } return output; } @catch (NSException *exception) { NSLog(@"Exception during image processing: %@", exception.reason); return nil; } } @end ``` ### 3. Memory Management ```objc theme={null} @interface SafeImageProcessor : NSObject @property (nonatomic, strong) FBBeautyEffectEngine *engine; @end @implementation SafeImageProcessor - (BOOL)processImageSafe:(FBImageFrame *)input output:(FBImageFrame *)output { FBImageFrame *result = nil; @try { // Process image result = [self.engine processImage:input processMode:FBProcessModeVideo]; if (result == nil) { NSLog(@"Processing returned nil"); return NO; } // Copy result to output [self copyImageFrame:result to:output]; return YES; } @catch (NSException *exception) { NSLog(@"Exception during processing: %@", exception.reason); return NO; } } - (void)copyImageFrame:(FBImageFrame *)src to:(FBImageFrame *)dst { // Implement image copying logic FBImageBuffer *srcBuffer = [src toRGBA]; FBImageBuffer *dstBuffer = [dst toRGBA]; if (srcBuffer != nil && dstBuffer != nil) { const uint8_t *srcData = [srcBuffer data]; uint8_t *dstData = (uint8_t *)[dstBuffer data]; if (srcData != nil && dstData != nil) { memcpy(dstData, srcData, MIN(srcBuffer.size, dstBuffer.size)); } } } @end ``` ## Logging and File Operations ### 1. Log Configuration Error Handling ```objc theme={null} @interface LogConfigHandler : NSObject + (BOOL)configureLogging:(BOOL)enableConsole enableFile:(BOOL)enableFile logFilePath:(NSString *)logFilePath; @end @implementation LogConfigHandler + (BOOL)configureLogging:(BOOL)enableConsole enableFile:(BOOL)enableFile logFilePath:(NSString *)logFilePath { @try { FBLogConfig *logConfig = [[FBLogConfig alloc] init]; logConfig.consoleEnabled = enableConsole; logConfig.fileEnabled = enableFile; logConfig.level = FBLogLevel_Info; if (enableFile && logFilePath != nil && logFilePath.length > 0) { // Check if directory exists NSString *logDir = [logFilePath stringByDeletingLastPathComponent]; NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:logDir]) { NSError *error = nil; if (![fileManager createDirectoryAtPath:logDir withIntermediateDirectories:YES attributes:nil error:&error]) { NSLog(@"Failed to create log directory: %@", error.localizedDescription); return NO; } } logConfig.fileName = logFilePath; } int ret = [FBBeautyEffectEngine setLogConfig:logConfig]; return ret == 0; } @catch (NSException *exception) { NSLog(@"Failed to configure logging: %@", exception.reason); return NO; } } @end ``` ### 2. File Write Error Handling ```objc theme={null} @interface FileWriteHandler : NSObject + (BOOL)saveImageBuffer:(FBImageBuffer *)buffer toPath:(NSString *)filePath; @end @implementation FileWriteHandler + (BOOL)saveImageBuffer:(FBImageBuffer *)buffer toPath:(NSString *)filePath { if (buffer == nil) { NSLog(@"ImageBuffer is nil"); return NO; } NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *dir = [filePath stringByDeletingLastPathComponent]; // Create directory if (![fileManager fileExistsAtPath:dir]) { NSError *error = nil; if (![fileManager createDirectoryAtPath:dir withIntermediateDirectories:YES attributes:nil error:&error]) { NSLog(@"Failed to create directory: %@", error.localizedDescription); return NO; } } // Write file const uint8_t *data = [buffer data]; NSData *nsData = [NSData dataWithBytes:data length:buffer.size]; NSError *error = nil; BOOL success = [nsData writeToFile:filePath options:NSDataWritingAtomic error:&error]; if (!success) { NSLog(@"Failed to write file: %@", error.localizedDescription); return NO; } NSLog(@"Image saved successfully: %@", filePath); return YES; } @end ``` ## Exception Handling ### 1. Catch and Handle Exceptions ```objc theme={null} @interface ExceptionHandler : NSObject + (void)safeExecute:(void(^)(void))block operation:(NSString *)operation; @end @implementation ExceptionHandler + (void)safeExecute:(void(^)(void))block operation:(NSString *)operation { @try { if (block != nil) { block(); } } @catch (NSInvalidArgumentException *exception) { NSLog(@"%@ failed: Invalid argument - %@", operation, exception.reason); } @catch (NSException *exception) { NSLog(@"%@ failed: Unexpected exception - %@", operation, exception.reason); } } @end ``` ### 2. Retry Mechanism ```objc theme={null} @interface RetryHandler : NSObject @property (nonatomic, strong) FBBeautyEffectEngine *engine; @end @implementation RetryHandler static const int MAX_RETRY = 3; static const NSTimeInterval RETRY_DELAY = 0.1; - (FBImageFrame *)processImageWithRetry:(FBImageFrame *)input { for (int i = 0; i < MAX_RETRY; i++) { @try { FBImageFrame *result = [self.engine processImage:input processMode:FBProcessModeVideo]; if (result != nil) { return result; } } @catch (NSException *exception) { NSLog(@"Attempt %d failed: %@", i + 1, exception.reason); } if (i < MAX_RETRY - 1) { [NSThread sleepForTimeInterval:RETRY_DELAY]; } } NSLog(@"All retry attempts failed"); return nil; } @end ``` ## Debugging Tips ### 1. Error Information Collection ```objc theme={null} @interface DebugInfoCollector : NSObject + (NSString *)collectErrorInfo:(int)errorCode operation:(NSString *)operation exception:(NSException *)exception; @end @implementation DebugInfoCollector + (NSString *)collectErrorInfo:(int)errorCode operation:(NSString *)operation exception:(NSException *)exception { NSMutableString *info = [NSMutableString string]; [info appendFormat:@"Operation: %@\n", operation]; [info appendFormat:@"Error Code: %d\n", errorCode]; [info appendFormat:@"Timestamp: %lld\n", (long long)([[NSDate date] timeIntervalSince1970] * 1000)]; if (exception != nil) { [info appendFormat:@"Exception: %@\n", exception.name]; [info appendFormat:@"Reason: %@\n", exception.reason]; } // Add memory information struct mach_task_basic_info taskInfo; mach_msg_type_number_t size = MACH_TASK_BASIC_INFO_COUNT; kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&taskInfo, &size); if (kerr == KERN_SUCCESS) { [info appendFormat:@"Memory - Resident: %llu MB\n", taskInfo.resident_size / 1024 / 1024]; [info appendFormat:@"Memory - Virtual: %llu MB\n", taskInfo.virtual_size / 1024 / 1024]; } return [info copy]; } @end ``` ### 2. Performance Monitoring ```objc theme={null} @interface PerformanceMonitor : NSObject @property (nonatomic, assign) NSTimeInterval startTime; @property (nonatomic, assign) NSInteger errorCount; @end @implementation PerformanceMonitor - (void)startOperation { self.startTime = [[NSDate date] timeIntervalSince1970]; } - (void)endOperation:(NSString *)operation success:(BOOL)success { NSTimeInterval duration = [[NSDate date] timeIntervalSince1970] - self.startTime; if (!success) { self.errorCount++; NSLog(@"%@ failed after %.2fms (Error count: %ld)", operation, duration * 1000, (long)self.errorCount); } else if (duration > 0.1) { NSLog(@"%@ took %.2fms (slow operation)", operation, duration * 1000); } } @end ``` ## Summary Following these error handling best practices can help you: 1. **Improve App Stability**: Through API return value checking and exception handling 2. **Improve User Experience**: Through friendly error prompts and recovery mechanisms 3. **Facilitate Problem Troubleshooting**: Through detailed logging and error information collection 4. **Optimize Performance**: Through reasonable error recovery strategies and resource management Remember to adjust error handling logic based on actual SDK error code definitions and continuously monitor and improve error handling mechanisms. The iOS platform uses ARC for automatic memory management, but still need to pay attention to avoiding circular references and releasing unnecessary resources timely. # FAQ Source: https://facebetter.mintlify.app/ios/faq iOS Beauty SDK Common Questions and Answers ## Integration Issues ### Q: What to do if engine creation fails? A: Check the following points: * Confirm that `appId` and `appKey` are correct * Check if network connection is normal * View log output for detailed error information * Confirm if SDK version is latest * Check if Framework is properly linked ### Q: Can't find header files or classes during compilation? A: Possible reasons: * Confirm that `Facebetter.framework` has been properly added to the project * Check if Framework's "Embed & Sign" setting is correct * Confirm import statement: `#import ` * Try Clean Build Folder and rebuild ### Q: Link errors at runtime? A: Solutions: * Confirm if device architecture is supported (arm64, armv7) * Check if Framework file is complete * Confirm Framework's deployment target setting is correct * Check for other Framework conflicts ## Functionality Issues ### Q: Beauty effects are not obvious? A: You can try: * Increase beauty parameter values (range 0.0-1.0) * Ensure corresponding beauty types are enabled * Check if image quality is clear enough * Confirm if face detection is working normally ### Q: Beauty effects are excessive or distorted? A: Recommendations: * Reduce beauty parameter values * Check if parameter combinations are reasonable * Avoid enabling too many beauty types simultaneously * Adjust parameters based on image quality ### Q: Virtual background not working? A: Check: * Confirm `FBBeautyType_VirtualBackground` is enabled * Check if background image path is correct * Confirm if image format is supported (PNG, JPG) * Check if image file exists and is readable ## Performance Issues ### Q: What to do if processing is slow? A: Optimization suggestions: * Use `FBFrameTypeVideo` for real-time processing * Reduce image resolution * Reduce number of simultaneously enabled beauty types * Avoid image processing on main thread * Use multi-threading for image processing ### Q: High memory usage? A: Solutions: * Release `FBImageFrame` and `FBImageBuffer` objects timely * Avoid frequent creation and destruction of engine instances * Reuse `FBImageFrame` objects * Use object pool pattern to manage image buffers ### Q: App crashes or freezes? A: Troubleshooting steps: * Check if image processing is done on main thread * Confirm resource release is complete * View exception information in logs * Check if parameter values are within valid range * Use Instruments tool to analyze memory and performance ## Image Processing Issues ### Q: How to handle different image formats? A: Use `FBImageFrame`'s format conversion methods: * `toRGBA` - Convert to RGBA format * `toI420` - Convert to I420 format * `toNV12` - Convert to NV12 format * `toBGRA` - Convert to BGRA format ### Q: Camera preview image processing? A: Recommended process: 1. Get CVPixelBuffer from AVCaptureSession 2. Use `createWithData` to create FBImageFrame 3. Call `processImage` to process 4. Convert to target format for display ### Q: Image rotation issues? A: Solutions: * Use `FBImageFrame`'s `rotate` method to rotate images * Supports 0°, 90°, 180°, 270° rotation * Rotation operation modifies original image data ## Permission Issues ### Q: Camera permission issues? A: Need to add permission description: ```xml theme={null} NSCameraUsageDescription Camera permission required for beauty photography ``` ### Q: Photo library permission issues? A: Need to add permission description: ```xml theme={null} NSPhotoLibraryUsageDescription Photo library permission required to select photos for beauty ``` ## Debugging Issues ### Q: How to enable debug logging? A: Configure before creating engine: ```objc theme={null} FBLogConfig *logConfig = [[FBLogConfig alloc] init]; logConfig.consoleEnabled = YES; logConfig.fileEnabled = YES; logConfig.level = FBLogLevel_Debug; [FBBeautyEffectEngine setLogConfig:logConfig]; ``` ### Q: How to get detailed error information? A: Methods: * Enable DEBUG level logging * Check API return values (0 indicates success) * View file log output * Use Xcode's Console to view logs ## Version Compatibility ### Q: Which iOS versions are supported? A: Supports iOS 10.0 and above ### Q: Which device architectures are supported? A: Supports: * arm64 (64-bit devices) * armv7 (32-bit devices, iOS 10.0+) ### Q: How to upgrade SDK version? A: Steps: 1. Download new version SDK 2. Replace old Framework file 3. Clean project cache 4. Rebuild project 5. Test if functionality works normally ## ARC Related Issues ### Q: How to handle memory management? A: iOS uses ARC, but need to pay attention to: * Set objects to nil timely * Avoid circular references * Use weak references to avoid strong reference cycles ### Q: How to handle CVPixelBuffer? A: Recommendations: * Use `CVPixelBufferRetain` and `CVPixelBufferRelease` for management * Or use `__bridge_transfer` and `__bridge_retained` for conversion ## Multi-threading Issues ### Q: How to process images in background thread? A: Recommendations: * Use `dispatch_async` to process in background queue * Avoid image processing on main thread * Use `dispatch_async(dispatch_get_main_queue())` to update UI ### Q: How to handle concurrent access? A: Solutions: * Use `@synchronized` to protect shared resources * Use serial queue for image processing * Avoid multi-threaded access to engine instance ## Deployment Issues ### Q: App Store review rejected? A: Possible reasons: * Missing necessary permission descriptions * Using private APIs * Memory leak issues * Crash issues ### Q: How to optimize app size? A: Recommendations: * Remove unused architectures (like armv7) * Compress resource files * Use App Thinning * Remove unused Framework parts # Implement Beauty Source: https://facebetter.mintlify.app/ios/implement-beauty Implement iOS Beauty ## Add SDK Dependency ### Method A: CocoaPods Integration (Recommended) Add the `Facebetter` dependency to your project's `Podfile`: ```ruby theme={null} target 'YourTargetName' do # Please replace with the latest version pod 'Facebetter', '1.2.2' end ``` Run the installation command: ```bash theme={null} pod install ``` **Xcode 15+ Compilation Error Handling** If you are using **Xcode 15** or later, you might encounter a `Sandbox: rsync.samba deny(1)` error during compilation. This is caused by Xcode's default **User Script Sandboxing** being enabled. **Solution:** 1. Select your **Project** in Xcode. 2. Navigate to the **Build Settings** tab. 3. Search for `ENABLE_USER_SCRIPT_SANDBOXING`. 4. Change its value from `Yes` to **`No`**. ### Method B: Manual Framework Integration Go to the [Download](https://facebetter.net/download) page to get the latest SDK, then extract it. Copy the `Facebetter.framework` library from the SDK package to your project path. Open Xcode and [refer to this guide](https://help.apple.com/xcode/mac/current/#/dev51a648b07) to add the `Facebetter.framework` dynamic library. Make sure the **Embed** property of the added dynamic library is set to **Embed & Sign**. Xcode Link Library ### Permission Configuration Add necessary permissions in `Info.plist`: ```xml theme={null} NSCameraUsageDescription Camera permission required for beauty photography ``` **Permission Descriptions:** * **Camera Permission**: Optional. Only needed when using camera capture for beauty processing in the app. Not required if only processing existing images. ## Import Header Files ```objc theme={null} #import ``` ## Log Configuration Logging is disabled by default and can be enabled as needed. Both console logging and file logging switches are supported. Logging should be enabled **before** creating the beauty engine, otherwise you may not see initialization logs. ```objc theme={null} FBLogConfig* logConfig = [[FBLogConfig alloc] init]; // Log level logConfig.level = FBLogLevel_Info; // Console logging logConfig.consoleEnabled = YES; // File logging logConfig.fileEnabled = YES; logConfig.fileName = @"log path: xx/xx/facebetter.log"; ``` ## Create Configuration Engine Follow the instructions on [this page](/intro/enable-service#get-appid-and-appkey) to get your `appid` and `appkey`. **Verification Priority:** * If `licenseJson` is provided, use license data verification (supports online response and offline license) * Otherwise, use `appId` and `appKey` for automatic online verification ```objc{2,3,4} theme={null} FBEngineConfig *engineConfig = [[FBEngineConfig alloc] init]; engineConfig.appId = @"your appId"; // Configure your appid (optional, not required if licenseJson is provided) engineConfig.appKey = @"your appkey"; // Configure your appkey (optional, not required if licenseJson is provided) // Optional: Use license data verification (takes priority if provided) // engineConfig.licenseJson = @"your license json string"; self.beautyEffectEngine = [FBBeautyEffectEngine createEngineWithConfig:engineConfig]; ``` ### Error Handling After creating the engine, it's recommended to check if it was successful: ```objc theme={null} if (self.beautyEffectEngine == nil) { NSLog(@"Failed to create beauty engine"); return; } ``` ## Adjust Beauty Parameters All beauty parameters range from `[0.0, 1.0]`. Set to `0` to disable the effect. ### Set Skin Beauty Parameters Use the `setBasicParam` interface to set skin beauty parameters. **Parameter range \[0.0, 1.0]**. ```objc theme={null} [self.beautyEffectEngine setBasicParam:FBBasicParam_Smoothing floatValue:0.5f]; ``` Supported skin beauty parameters: ```objc theme={null} typedef NS_ENUM(NSInteger, FBBasicParam) { FBBasicParam_Smoothing = 0, // Smoothing FBBasicParam_Sharpening, // Sharpening FBBasicParam_Whitening, // Whitening FBBasicParam_Rosiness, // Rosiness }; ``` ### Set Skin-Only Beauty Use the `setSkinOnlyBeauty:` interface to set whether beauty effects are applied only to skin regions. When enabled, beauty effects (smoothing, whitening, etc.) will only be applied to detected skin areas, leaving non-skin areas unchanged. ```objc theme={null} // Enable skin-only beauty [self.beautyEffectEngine setSkinOnlyBeauty:YES]; // Disable skin-only beauty (apply to entire image) [self.beautyEffectEngine setSkinOnlyBeauty:NO]; ``` After enabling skin-only beauty, even with high beauty parameter values, non-skin areas (such as background, clothing, etc.) will not be affected. ### Set Face Reshape Parameters Use the `setReshapeParam` interface to set face reshape parameters. **Parameter range \[0.0, 1.0]**. ```objc theme={null} [self.beautyEffectEngine setReshapeParam:FBReshapeParam_FaceThin floatValue:0.5f]; ``` Supported face reshape parameters: ```objc theme={null} typedef NS_ENUM(NSInteger, FBReshapeParam) { FBReshapeParam_FaceThin = 0, // Face thinning FBReshapeParam_FaceVShape, // V-shaped face FBReshapeParam_FaceNarrow, // Narrow face FBReshapeParam_FaceShort, // Short face FBReshapeParam_Cheekbone, // Cheekbone FBReshapeParam_Jawbone, // Jawbone FBReshapeParam_Chin, // Chin FBReshapeParam_NoseSlim, // Nose slimming FBReshapeParam_EyeSize, // Eye enlargement FBReshapeParam_EyeDistance, // Eye distance }; ``` ### Set Makeup Parameters ```objc theme={null} [self.beautyEffectEngine setMakeupParam:FBMakeupParam_Lipstick floatValue:0.5f]; ``` Supported makeup parameters: ```objc theme={null} typedef NS_ENUM(NSInteger, FBMakeupParam) { FBMakeupParam_Lipstick = 0, // Lipstick FBMakeupParam_Blush, // Blush }; ``` ### Set Virtual Background Enable virtual background through the `setVirtualBackground` interface: ```objc theme={null} // Set background mode FBVirtualBackgroundOptions *options = [[FBVirtualBackgroundOptions alloc] initWithMode:FBBackgroundModeBlur]; [self.beautyEffectEngine setVirtualBackground:options]; // Set background image (need to set to Image mode first) FBVirtualBackgroundOptions *imageOptions = [[FBVirtualBackgroundOptions alloc] initWithMode:FBBackgroundModeImage]; imageOptions.backgroundImage = backgroundImageFrame; // FBImageFrame object [self.beautyEffectEngine setVirtualBackground:imageOptions]; ``` ## Using Filters and Stickers ### Filter Functionality Filters are set through the `setFilter:` interface. Filter resource files (`.fbd`) must be registered via `registerFilter:fbdFilePath:` first. ```objc theme={null} // 1. Register filter resource NSString *filterId = @"chuxin"; NSString *fbdPath = [[NSBundle mainBundle] pathForResource:@"chuxin" ofType:@"fbd"]; [self.beautyEffectEngine registerFilter:filterId fbdFilePath:fbdPath]; // 2. Use filter [self.beautyEffectEngine setFilter:filterId]; // 3. Adjust filter intensity (0.0 - 1.0) [self.beautyEffectEngine setFilterIntensity:0.8f]; ``` ### Sticker Functionality Stickers are set through the `setSticker:` interface and also need to be registered first. ```objc theme={null} // 1. Register sticker resource NSString *stickerId = @"cherry"; NSString *fbdPath = [[NSBundle mainBundle] pathForResource:@"cherry" ofType:@"fbd"]; [self.beautyEffectEngine registerSticker:stickerId fbdFilePath:fbdPath]; // 2. Use sticker [self.beautyEffectEngine setSticker:stickerId]; // 3. Clear sticker [self.beautyEffectEngine setSticker:@""]; ``` ## Set Engine Callbacks Monitor engine events (license validation and engine initialization status): ```objc theme={null} FBEngineCallbacks *callbacks = [[FBEngineCallbacks alloc] init]; callbacks.onEngineEvent = ^(FBEngineEventCode code, NSString* _Nullable message) { if (code == FBEngineEventCodeLicenseValidationSuccess) { // License validation succeeded NSLog(@"License validation succeeded"); } else if (code == FBEngineEventCodeLicenseValidationFailed) { // License validation failed NSLog(@"License validation failed: %@", message); } else if (code == FBEngineEventCodeInitializationComplete) { // Engine initialization completed NSLog(@"Engine initialization completed"); } else if (code == FBEngineEventCodeInitializationFailed) { // Engine initialization failed NSLog(@"Engine initialization failed: %@", message); } }; [self.beautyEffectEngine setCallbacks:callbacks]; ``` Event codes: * `FBEngineEventCodeLicenseValidationSuccess` (0): License validation succeeded * `FBEngineEventCodeLicenseValidationFailed` (1): License validation failed * `FBEngineEventCodeInitializationComplete` (100): Engine initialization completed * `FBEngineEventCodeInitializationFailed` (101): Engine initialization failed ## Process Images ### Create Images Image data is encapsulated through `FBImageFrame`, supporting formats: `YUVI420`, `NV12`, `NV21`, `RGB`, `RGBA`, `BGR`, `BGRA`. **Create FBImageFrame with RGBA** ```objc theme={null} FBImageFrame *input_image = [FBImageFrame createWithRGBA:data width:width height:height stride:stride]; ``` **Create FBImageFrame with image file** ```objc theme={null} FBImageFrame *input_image = [FBImageFrame createWithFile:@"xxx.png"]; ``` ### Rotate Images FBImageFrame has built-in image rotation methods that can be used as needed. ```objc theme={null} - (int)rotate:(FBImageRotation)rotation; ``` Rotation angles ```objc theme={null} typedef NS_ENUM(NSInteger, FBImageRotation) { FBImageRotation0, // 0 degrees FBImageRotation90, // Clockwise 90 degrees FBImageRotation180, // Clockwise 180 degrees FBImageRotation270, // Clockwise 270 degrees }; ``` ### Process Images `processMode` includes Video and Image modes. Video mode is suitable for live streaming and video scenarios with higher efficiency. Image mode is suitable for image processing scenarios. ```objc theme={null} input_image.type = FBFrameTypeVideo; FBImageFrame *output_image = [self.beautyEffectEngine processImage:input_image]; ``` The engine automatically maintains input/output format consistency. If input is RGBA format, output is RGBA format; if input is I420 format, output is I420 format. ### Get Processed Image Data ```objc theme={null} FBImageBuffer* buffer = [output_image toRGBA]; uint8_t* data = [buffer data]; int data_size = buffer.size; int width = buffer.width; int height = buffer.width; int stride = buffer.stride; ``` Get I420 data ```objc theme={null} FBImageBuffer* buffer = [output_image toI420]; // Get continuous I420 memory data uint8_t* data = [buffer data]; // Get I420 data length int data_size = buffer.size; // Get Y, U, V component data separately uint8_t* dataY = [buffer dataY]; uint8_t* dataU = [buffer dataU]; uint8_t* dataV = [buffer dataV]; int strideY = buffer.strideY; int strideU = buffer.strideU; int strideV = buffer.strideV; ``` `FBImageFrame` can be converted to various formats through built-in toXXX methods: `YUVI420`, `NV12`, `NV21`, `RGB`, `RGBA`, `BGR`, `BGRA`. These methods can be used for format conversion. ## External Texture Processing When using external texture processing, you must ensure the OpenGL ES context is on the main thread and pass `externalContext = YES` during engine initialization. ### Use Cases External texture processing is suitable for the following scenarios: * **OpenGL ES/Metal Rendering Pipeline Integration**: When your application already uses OpenGL ES or Metal for rendering, you can directly use textures as input and output, avoiding CPU-GPU data copying * **Real-time Video Processing**: Process textures directly in video rendering callbacks to reduce memory copy overhead * **Performance Optimization**: Avoid downloading texture data to CPU memory and uploading back to GPU, improving processing efficiency ### Configure External Context When using external texture processing, you need to enable the `externalContext` option when creating the engine: ```objc theme={null} FBEngineConfig *engineConfig = [[FBEngineConfig alloc] init]; engineConfig.appId = @"your appId"; engineConfig.appKey = @"your appKey"; engineConfig.externalContext = YES; // Enable external context mode self.beautyEffectEngine = [FBBeautyEffectEngine createEngineWithConfig:engineConfig]; ``` **Important Notes**: * When `externalContext = YES`, the engine will not create its own OpenGL context, but use the current thread's OpenGL context * The engine must be created in a valid OpenGL context * Input and output textures must be in the same OpenGL context ### Create Texture Frame Use the `FBImageFrame.createWithTexture:` method to create an image frame from an OpenGL texture: ```objc theme={null} // Create FBImageFrame from OpenGL texture GLuint textureId = ...; // Your OpenGL texture ID int width = 1920; int height = 1080; int stride = width * 4; // RGBA format, 4 bytes per pixel FBImageFrame *inputFrame = [FBImageFrame createWithTexture:textureId width:width height:height stride:stride]; if (!inputFrame) { NSLog(@"Failed to create FBImageFrame from texture"); return; } ``` **Parameter Description**: * `textureId`: OpenGL texture ID (type `GL_TEXTURE_2D`) * `width`: Texture width (pixels) * `height`: Texture height (pixels) * `stride`: Row stride (bytes), usually `width * 4` (RGBA format) ### Get Output Texture After processing the image, you can get the output texture through the `FBImageBuffer.texture` property: ```objc theme={null} // Process image FBImageFrame *outputFrame = [self.beautyEffectEngine processImage:inputFrame processMode:FBProcessModeImage]; if (!outputFrame) { NSLog(@"processImage returned nil"); return; } // Get output texture FBImageBuffer *textureBuffer = [outputFrame getBuffer]; if (!textureBuffer) { NSLog(@"getBuffer returned nil"); return; } // Get output texture ID and dimensions GLuint outputTextureId = textureBuffer.texture; int outputWidth = textureBuffer.width; int outputHeight = textureBuffer.height; ``` ### Complete Example ```objc theme={null} @interface ExternalTextureViewController () @property (nonatomic, strong) FBBeautyEffectEngine *engine; @end @implementation ExternalTextureViewController - (int)processVideoFrame:(GLuint)inputTexture width:(int)width height:(int)height outputTexture:(GLuint *)outputTexture { // Lazy initialize engine (in OpenGL context) if (!self.engine) { FBEngineConfig *config = [[FBEngineConfig alloc] init]; config.appId = @"your appId"; config.appKey = @"your appKey"; config.externalContext = YES; // Key: enable external context self.engine = [FBBeautyEffectEngine createEngineWithConfig:config]; [self.engine setBasicParam:FBBasicParam_Smoothing floatValue:0.5f]; } // Create FBImageFrame from input texture int stride = width * 4; FBImageFrame *inputFrame = [FBImageFrame createWithTexture:inputTexture width:width height:height stride:stride]; if (!inputFrame) { return -1; } // Process image FBImageFrame *outputFrame = [self.engine processImage:inputFrame processMode:FBProcessModeImage]; if (!outputFrame) { return -2; } // Get output texture FBImageBuffer *textureBuffer = [outputFrame getBuffer]; if (!textureBuffer) { return -3; } // Return output texture ID *outputTexture = textureBuffer.texture; return 0; // Success } @end ``` ### Important Notes #### 1. Context Requirements * **Engine must be created in a valid OpenGL context**: When `externalContext = YES`, the engine uses the current thread's OpenGL context, so the engine must be created in the OpenGL rendering thread * **Context consistency**: Input texture, engine processing, and output texture must be in the same OpenGL context * **Thread safety**: OpenGL operations must be executed in the same thread #### 2. Texture Format Requirements * **Input texture format**: Supports `GL_RGBA` format `GL_TEXTURE_2D` textures * **Texture parameters**: It is recommended to set the following texture parameters for best results: ```objc theme={null} glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); ``` #### 3. Performance Optimization * **Lazy initialization**: Initialize the engine in the first rendering callback to ensure it is created in the correct OpenGL context * **Reuse FBImageFrame**: If possible, reuse `FBImageFrame` objects to reduce object creation overhead * **Process mode selection**: * `FBProcessModeVideo`: Suitable for real-time video stream processing, higher performance * `FBProcessModeImage`: Suitable for single frame image processing, better quality #### 4. Memory Management * **ARC automatic management**: iOS uses ARC for automatic memory management, but still need to pay attention to releasing unused objects in time * **Texture lifecycle**: Output textures are managed by the engine and do not need manual deletion, but input textures need to be managed by the caller * **Avoid circular references**: When using `self` in blocks or callbacks, pay attention to using `__weak` to avoid circular references #### 5. Error Handling * **Check return values**: All API calls should check return values * **Nil pointer checks**: Check if return values of `createWithTexture:` and `processImage:` are `nil` * **Texture validity**: Ensure input texture ID is valid and bound to the current OpenGL context #### 6. Common Issues * **Engine creation failure**: Check if it is created in an OpenGL context and if `externalContext` is correctly set * **Texture processing failure**: Check if texture format is RGBA and texture parameters are correctly set * **Context loss**: If the OpenGL context is destroyed, the engine needs to be recreated ## Lifecycle Management FBBeautyEffectEngine is a singleton and is automatically released when the app ends. Manual management is not required. ### Release Resources When ViewController is destroyed, be sure to release engine resources: ```objc theme={null} - (void)dealloc { if (self.beautyEffectEngine) { // Note: FBBeautyEffectEngine is a singleton, usually doesn't need manual release // But if there are custom cleanup logic, it can be handled here self.beautyEffectEngine = nil; } } ``` ### Memory Management * Release `FBImageFrame` and `FBImageBuffer` objects timely * Avoid repeatedly creating large numbers of image objects in loops * Recommend reusing `FBImageFrame` objects ```objc theme={null} // Release resources after use if (inputImage) { inputImage = nil; // ARC will automatically release } if (outputImage) { outputImage = nil; // ARC will automatically release } if (buffer) { buffer = nil; // ARC will automatically release } ``` ## Related Documentation * [Best Practices](/ios/best-practices) - Performance optimization and architecture design recommendations * [Common Issues](/ios/faq) - Common questions and troubleshooting * [API Reference](/ios/api-reference) - Complete API documentation # Quick Start Source: https://facebetter.mintlify.app/ios/quick-start Run iOS Sample Application ## Environment Requirements * **iOS Version**: `iOS 12.0` and above * **Development Language**: `Objective-C` * **Architecture Support**: `arm64` ## Get Sample Source Code Clone the [GitHub repository](https://github.com/pixpark/facebetter-sdk) locally, and navigate to the `demo/ios` directory. ```bash theme={null} # clone git clone https://github.com/pixpark/facebetter-sdk.git # Enter iOS Demo directory cd facebetter-sdk/demo/ios # Install dependencies pod install ``` **Xcode 15+ Compilation Error Handling** If you are using **Xcode 15** or later, you might encounter a `Sandbox: rsync.samba deny(1)` error during compilation. This is caused by Xcode's default **User Script Sandboxing** being enabled. **Solution:** 1. Select your **Project** in Xcode. 2. Navigate to the **Build Settings** tab. 3. Search for `ENABLE_USER_SCRIPT_SANDBOXING`. 4. Change its value from `Yes` to **`No`**. ## Configure Application Information and Keys ### Bind Application Bundle ID Follow the instructions on [this page](/intro/enable-service#bind-application-information) to bind your iOS application Bundle ID in the console, for example: `com.example.app` ### Get AppID and AppKey Follow the instructions on [this page](/intro/enable-service#get-appid-and-appkey) to get your `appid` and `appkey`. Open `ViewController.m` in the project and modify the `appid` and `appkey`. ```objc theme={null} FBEngineConfig *engineConfig = [[FBEngineConfig alloc] init]; engineConfig.appId = @"your appId"; engineConfig.appKey = @"your appkey"; // Optional: If licenseJson is provided, license data verification takes priority, appId and appKey are not required // engineConfig.licenseJson = @"your license json string"; self.beautyEffectEngine = [FBBeautyEffectEngine createEngineWithConfig:engineConfig]; ``` `licenseJson` takes priority over `appId` + `appKey`. For offline download and details, see [License & Auth](/intro/license). ## Run the Project Open `demo/ios/FBExampleObjc.xcworkspace` 1. Ensure Xcode project signing is correct Xcode Signing 2. Build and run Select a physical device and click the build and run button. # API Reference Source: https://facebetter.mintlify.app/linux/api-reference Facebetter SDK C++ API reference for Linux The Linux C++ SDK API is **identical** to the Windows API. Please refer to: 👉 [Windows C++ API Reference](/windows/api-reference) The sections below cover Linux-specific notes only. *** ## Linux-Specific Notes ### Shared Library Loading The SDK ships as a shared library `libfacebetter.so`. At runtime the OS must be able to locate it: ```bash theme={null} # Option 1: LD_LIBRARY_PATH (recommended during development) export LD_LIBRARY_PATH=/path/to/facebetter-sdk/lib:$LD_LIBRARY_PATH ./your_app # Option 2: Add to ldconfig (system-wide installation) echo "/path/to/facebetter-sdk/lib" | sudo tee /etc/ld.so.conf.d/facebetter.conf sudo ldconfig # Option 3: Copy to a system library directory sudo cp libfacebetter.so /usr/local/lib/ sudo ldconfig ``` ### CMake Linkage ```cmake theme={null} target_include_directories(your_target PRIVATE /path/to/facebetter-sdk/include ) target_link_libraries(your_target PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lib/libfacebetter.so # OpenGL dependencies GL glfw ) ``` ### OpenGL Environment A valid OpenGL context is required. Install the necessary graphics libraries first: ```bash theme={null} # Ubuntu / Debian sudo apt install libgl1-mesa-dev libglu1-mesa-dev libglfw3-dev # Fedora / RHEL sudo dnf install mesa-libGL-devel mesa-libGLU-devel glfw-devel ``` For headless servers (no display), use Mesa's off-screen rendering: ```bash theme={null} # Ubuntu / Debian sudo apt install libosmesa6-dev ``` ### `external_context` Field If your application already manages an OpenGL context (e.g. via GLFW), set: ```cpp theme={null} EngineConfig cfg; cfg.external_context = true; // Use the caller's current GL context ``` When `true`, the SDK does not create an internal GL context; all GPU calls run on the calling thread's active context. ### `SetRenderView` Not Available This method exists only on iOS and macOS. Do not call it on Linux. ### DISPLAY Environment Variable When using a windowed GLFW display, an X11 or Wayland server must be present: ```bash theme={null} echo $DISPLAY # should print something like :0 or :1 ``` For remote SSH sessions, enable X11 forwarding: ```bash theme={null} ssh -X user@host ``` *** ## Full API Documentation For the complete reference covering all methods, enumerations, and data structures, see the [Windows C++ API Reference](/windows/api-reference) — it applies equally to Linux. # Best Practices Source: https://facebetter.mintlify.app/linux/best-practices Best practices and performance optimization for Linux beauty SDK # Error Handling Source: https://facebetter.mintlify.app/linux/error-handling Facebetter SDK error handling and troubleshooting guide for Linux Linux uses the same C++ API as Windows, so most error-handling logic is identical. This page also covers Linux-specific issues. ## Return Value Convention | Return value | Meaning | | ------------ | ------------------------------------------ | | `0` | Success | | Non-zero | Failure — check the log output for details | `BeautyEffectEngine::Create()` returns `nullptr` on failure. *** ## Common Errors and Fixes ### 1. Engine Creation Fails (`Create` Returns `nullptr`) The causes and fixes are the same as on Windows. Start by enabling logging: ```cpp theme={null} LogConfig log_cfg; log_cfg.console_enabled = true; log_cfg.level = LogLevel::Debug; BeautyEffectEngine::SetLogConfig(log_cfg); auto engine = BeautyEffectEngine::Create(eng_cfg); if (!engine) { std::cerr << "[Error] Engine creation failed." << std::endl; return -1; } ``` **Common causes:** invalid `app_id` / `app_key`, wrong `resource.fbd` path, no network access. *** ### 2. `libfacebetter.so` Not Found at Runtime **Error message:** ``` ./facebetter_demo: error while loading shared libraries: libfacebetter.so: cannot open shared object file: No such file or directory ``` **Fix (choose one):** ```bash theme={null} # Option A: set library path at runtime LD_LIBRARY_PATH=/path/to/sdk/lib ./facebetter_demo # Option B: copy the library next to the executable cp sdk/lib/libfacebetter.so build/ cd build && ./facebetter_demo # Option C: add to the system library config echo "/path/to/sdk/lib" | sudo tee /etc/ld.so.conf.d/facebetter.conf sudo ldconfig ./facebetter_demo ``` *** ### 3. `ProcessImage` Returns `nullptr` ```cpp theme={null} auto input = ImageFrame::CreateWithFile("input.jpg"); if (!input || !input->Data()) { std::cerr << "[Error] Failed to load input image." << std::endl; return; } auto output = engine->ProcessImage(input); if (!output || !output->Data()) { std::cerr << "[Error] ProcessImage failed." << std::endl; return; } ``` *** ### 4. Beauty Effects Have No Visible Impact Make sure each `BeautyType` is enabled and the parameter value is greater than `0`: ```cpp theme={null} engine->SetBeautyTypeEnabled(BeautyType::Basic, true); engine->SetBeautyTypeEnabled(BeautyType::Reshape, true); engine->SetBeautyTypeEnabled(BeautyType::Makeup, true); engine->SetBeautyTypeEnabled(BeautyType::Sticker, true); engine->SetBeautyParam(Basic::Smoothing, 0.5f); ``` *** ### 5. GLFW Cannot Open Display **Error message:** ``` Error: GLFW: X11: Display variable not set ``` **Fix:** ```bash theme={null} # Confirm you are in a graphical session echo $DISPLAY # should output something like :0 or :1 # If empty, set it manually (Xorg) export DISPLAY=:0 ./facebetter_demo # For Wayland export WAYLAND_DISPLAY=wayland-0 ./facebetter_demo ``` *** ### 6. OpenGL Initialization Failure **Possible cause:** Missing OpenGL driver or Mesa libraries. **Fix:** ```bash theme={null} # Ubuntu / Debian sudo apt install libgl1-mesa-glx libglu1-mesa # Fedora / RHEL sudo dnf install mesa-libGL mesa-libGLU # Verify OpenGL support (requires 3.0+) glxinfo | grep "OpenGL version" ``` *** ## Enabling Debug Logs ```cpp theme={null} LogConfig log_cfg; log_cfg.console_enabled = true; log_cfg.file_enabled = true; log_cfg.level = LogLevel::Debug; log_cfg.file_name = "facebetter.log"; BeautyEffectEngine::SetLogConfig(log_cfg); ``` *** ## Error Code Reference | Code | Meaning | | ---- | ----------------------- | | `0` | Success | | `-1` | Engine not initialized | | `-2` | Invalid parameter | | `-3` | Resource file not found | | `-4` | Processing failed | # FAQ Source: https://facebetter.mintlify.app/linux/faq Common questions and solutions for Linux beauty SDK integration # Implement Beauty Source: https://facebetter.mintlify.app/linux/implement-beauty Integrate the Facebetter SDK on Linux using the C++ interface On Linux, the Facebetter SDK is accessed through the same C++ interface as Windows. This guide follows the [C++ Desktop Demo](https://github.com/pixpark/facebetter-sdk) as a reference. ## Include Headers ```cpp theme={null} #include #include #include #include using namespace facebetter; using namespace facebetter::beauty_params; ``` ## Integration Overview ``` Configure logging → Create engine → Enable beauty types → Set params → Process frames → Render result ``` *** ## 1. Configure Logging (optional) Call this before creating the engine. ```cpp theme={null} LogConfig log_cfg; log_cfg.console_enabled = true; log_cfg.file_enabled = false; log_cfg.level = LogLevel::Info; BeautyEffectEngine::SetLogConfig(log_cfg); ``` Log levels (ascending): `Trace` / `Debug` / `Info` / `Warn` / `Error` / `Critical`. *** ## 2. Create the Engine ```cpp theme={null} EngineConfig eng_cfg; eng_cfg.app_id = "your_app_id"; eng_cfg.app_key = "your_app_key"; eng_cfg.resource_path = "resource/resource.fbd"; eng_cfg.external_context = false; std::shared_ptr engine = BeautyEffectEngine::Create(eng_cfg); if (!engine) { return -1; } ``` > At runtime, make sure `libfacebetter.so` is findable: place it next to the executable or point `LD_LIBRARY_PATH` to its directory. *** ## 3. Enable Beauty Types All beauty types are **disabled by default** and must be explicitly enabled: ```cpp theme={null} engine->SetBeautyTypeEnabled(BeautyType::Basic, true); engine->SetBeautyTypeEnabled(BeautyType::Reshape, true); engine->SetBeautyTypeEnabled(BeautyType::Makeup, true); engine->SetBeautyTypeEnabled(BeautyType::Sticker, true); ``` *** ## 4. Set Beauty Parameters All parameter values are in `[0.0, 1.0]`. A value of `0` disables the effect. ### Basic Beauty ```cpp theme={null} engine->SetBeautyParam(Basic::Smoothing, 0.5f); engine->SetBeautyParam(Basic::Whitening, 0.3f); engine->SetBeautyParam(Basic::Rosiness, 0.2f); engine->SetBeautyParam(Basic::Sharpening, 0.4f); ``` ### Skin-Only Beauty Use `SetSkinOnlyBeauty` to set whether beauty effects are applied only to skin regions. When enabled, beauty effects (smoothing, whitening, etc.) will only be applied to detected skin areas, leaving non-skin areas unchanged. ```cpp theme={null} // Enable skin-only beauty engine->SetSkinOnlyBeauty(true); // Disable skin-only beauty (apply to entire image) engine->SetSkinOnlyBeauty(false); ``` After enabling skin-only beauty, even with high beauty parameter values, non-skin areas (such as background, clothing, etc.) will not be affected. ### Face Reshape ```cpp theme={null} engine->SetBeautyParam(Reshape::FaceThin, 0.4f); engine->SetBeautyParam(Reshape::FaceVShape, 0.3f); engine->SetBeautyParam(Reshape::FaceNarrow, 0.2f); engine->SetBeautyParam(Reshape::FaceShort, 0.2f); engine->SetBeautyParam(Reshape::Cheekbone, 0.3f); engine->SetBeautyParam(Reshape::Jawbone, 0.2f); engine->SetBeautyParam(Reshape::Chin, 0.2f); engine->SetBeautyParam(Reshape::NoseSlim, 0.3f); engine->SetBeautyParam(Reshape::EyeSize, 0.4f); engine->SetBeautyParam(Reshape::EyeDistance, 0.1f); ``` ### Makeup ```cpp theme={null} engine->SetBeautyParam(Makeup::Lipstick, 0.6f); engine->SetBeautyParam(Makeup::Blush, 0.4f); ``` ### Stickers ```cpp theme={null} engine->SetSticker("rabbit"); // enable sticker engine->SetSticker(""); // disable sticker ``` ### Filters (LUT) ```cpp theme={null} engine->SetFilter("chuxin"); engine->SetFilterIntensity(0.8f); engine->SetFilter(""); // disable filter ``` *** ## 5. Process Frames ```cpp theme={null} // Create input frame from in-memory RGBA data auto input_frame = ImageFrame::CreateWithRGBA( rgba_data, width, height, stride); input_frame->type = FrameType::Video; auto output_frame = engine->ProcessImage(input_frame); if (output_frame && output_frame->Data()) { const uint8_t* data = output_frame->Data(); int w = output_frame->Width(); int h = output_frame->Height(); } ``` **Load from file** (single-image mode): ```cpp theme={null} auto input_frame = ImageFrame::CreateWithFile("input.jpg"); input_frame->type = FrameType::Image; auto output_frame = engine->ProcessImage(input_frame); ``` **Convert output format** if needed: ```cpp theme={null} auto bgra_frame = output_frame->Convert(Format::BGRA); auto i420_frame = output_frame->Convert(Format::I420); ``` *** ## 6. OpenGL Integration (render preview) ```cpp theme={null} // Create texture (once) GLuint tex; glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Upload per frame glBindTexture(GL_TEXTURE_2D, tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, output_frame->Width(), output_frame->Height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, output_frame->Data()); // Render with ImGui ImGui::Image( static_cast(static_cast(tex)), ImVec2(img_w, img_h)); ``` *** ## 7. Full Example (real-time processing loop) ```cpp theme={null} #include #include #include #include #include using namespace facebetter; using namespace facebetter::beauty_params; // Initialization LogConfig log_cfg; log_cfg.console_enabled = true; log_cfg.level = LogLevel::Info; BeautyEffectEngine::SetLogConfig(log_cfg); EngineConfig eng_cfg; eng_cfg.app_id = "your_app_id"; eng_cfg.app_key = "your_app_key"; eng_cfg.resource_path = "resource/resource.fbd"; eng_cfg.external_context = false; auto engine = BeautyEffectEngine::Create(eng_cfg); engine->SetBeautyTypeEnabled(BeautyType::Basic, true); engine->SetBeautyTypeEnabled(BeautyType::Reshape, true); engine->SetBeautyTypeEnabled(BeautyType::Makeup, true); engine->SetBeautyTypeEnabled(BeautyType::Sticker, true); engine->SetBeautyParam(Basic::Smoothing, 0.5f); engine->SetBeautyParam(Reshape::FaceThin, 0.4f); engine->SetBeautyParam(Makeup::Lipstick, 0.6f); // Processing loop (~30 fps) double last_time = 0.0; const double kInterval = 1.0 / 30.0; GLuint preview_tex = 0; while (!glfwWindowShouldClose(window)) { glfwPollEvents(); double now = glfwGetTime(); if (now - last_time >= kInterval) { auto input = ImageFrame::CreateWithRGBA( rgba_buffer, width, height, stride); input->type = FrameType::Video; auto output = engine->ProcessImage(input); if (output && output->Data()) { if (preview_tex == 0) { glGenTextures(1, &preview_tex); glBindTexture(GL_TEXTURE_2D, preview_tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } glBindTexture(GL_TEXTURE_2D, preview_tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, output->Width(), output->Height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, output->Data()); } last_time = now; } // ... ImGui render ... } // Cleanup if (preview_tex) glDeleteTextures(1, &preview_tex); engine.reset(); ``` *** ## Quick Reference The parameter enums and `ImageFrame` factory methods are identical to the Windows platform. See [Windows – Implement Beauty](../windows/implement-beauty#quick-reference) for the full table. # Quick Start Source: https://facebetter.mintlify.app/linux/quick-start Build and run the Facebetter C++ desktop demo on Linux (GLFW + ImGui) This guide explains how to build and run the Facebetter C++ desktop demo on Linux. The demo is built with **GLFW + Dear ImGui + OpenGL 3**: the left panel shows a live beauty-processed preview, while the right panel provides sliders to control each effect in real time. ## Requirements | Tool | Version | | --------------------------- | -------------------------------------------------------------------------- | | GCC or Clang | C++17 support required (GCC 7+ / Clang 5+) | | CMake | 3.16+ | | Ninja | Any recent version (`apt install ninja-build` / `dnf install ninja-build`) | | OpenGL dev libraries | `libgl1-mesa-dev` (Ubuntu/Debian) or `mesa-libGL-devel` (Fedora/RHEL) | | X11 / Wayland dev libraries | Required by GLFW (see install commands below) | ### Install Dependencies (Ubuntu / Debian) ```bash theme={null} sudo apt update sudo apt install -y build-essential cmake ninja-build \ libgl1-mesa-dev libglu1-mesa-dev \ libx11-dev libxrandr-dev libxinerama-dev \ libxcursor-dev libxi-dev ``` ### Install Dependencies (Fedora / RHEL) ```bash theme={null} sudo dnf install -y gcc-c++ cmake ninja-build \ mesa-libGL-devel mesa-libGLU-devel \ libX11-devel libXrandr-devel libXinerama-devel \ libXcursor-devel libXi-devel ``` ## Step 1: Clone the Repository ```bash theme={null} git clone https://github.com/pixpark/facebetter-sdk.git cd facebetter-sdk ``` ## Step 2: Place the SDK Files Place the Linux SDK files under `demo/cpp/sdk/`: ``` demo/cpp/sdk/ ├── include/ │ └── facebetter/ │ ├── beauty_effect_engine.h │ ├── beauty_params.h │ ├── image_frame.h │ └── type_defines.h ├── lib/ │ └── libfacebetter.so ← shared library └── resource/ └── resource.fbd ← model and resource pack ``` The SDK download link is available on the [Download](https://facebetter.net/download) page or in your dashboard. ## Step 3: Build ```bash theme={null} cd demo/cpp cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=Release cmake --build build ``` After the build, CMake automatically copies `resource/resource.fbd` and `demo.png` (if present) to `build/resource/`. ## Step 4: Prepare a Preview Image (optional) Place any face photo named `demo.png` in `demo/cpp/`: ``` demo/cpp/demo.png ``` The engine will process the image at \~30 fps and display the result in the left panel. ## Step 5: Run ```bash theme={null} cd demo/cpp/build # Option A: add the library path at runtime LD_LIBRARY_PATH=../sdk/lib ./facebetter_demo # Option B: copy the shared library next to the executable cp ../sdk/lib/libfacebetter.so . ./facebetter_demo ``` Once running, the window shows: * **Left panel** – live beauty-processed preview * **Right panel** – Beauty Control Panel: * **Basic Beauty**: Smoothing / Whitening / Rosiness / Sharpening * **Face Reshape**: Face Thin / V Face / Narrow Face / Short Face / Cheekbone / Jawbone / Chin / Nose Slim / Eye Size / Eye Distance * **Makeup**: Lipstick / Blush * **Sticker**: dropdown (Off / rabbit) Click **Reset All** to restore all parameters to zero. ## Troubleshooting **Q: `libfacebetter.so: cannot open shared object file`**\ A: Run with `LD_LIBRARY_PATH=../sdk/lib ./facebetter_demo`, copy the library to the executable directory, or add the path to `/etc/ld.so.conf` and run `ldconfig`. **Q: CMake cannot find OpenGL**\ A: Install `libgl1-mesa-dev` (Ubuntu) or `mesa-libGL-devel` (Fedora), then re-run CMake. **Q: Window shows "Put demo.png …"**\ A: Place a face photo named `demo.png` in `demo/cpp/` and restart the demo. **Q: `GLFW: X11: Display variable not set`**\ A: Make sure you are running in a graphical session, or set the `DISPLAY` environment variable. # API Reference Source: https://facebetter.mintlify.app/macos/api-reference macOS API Reference ## Logging Related ### Log Levels Log level enumeration for controlling log output levels. ```objc theme={null} typedef NS_ENUM(NSInteger, FBLogLevel) { FBLogLevel_Trace = 0, FBLogLevel_Debug, FBLogLevel_Info, FBLogLevel_Warn, FBLogLevel_Error, FBLogLevel_Critical, }; ``` ### Log Configuration Class Log configuration class for configuring log output methods and levels. **Field Descriptions:** * `consoleEnabled`: Whether to enable console output * `fileEnabled`: Whether to enable file output * `level`: Log level * `fileName`: Log file path (only effective when `fileEnabled` is `YES`) ```objc theme={null} FB_OBJC_API @interface FBLogConfig : NSObject @property(nonatomic, assign) BOOL consoleEnabled; @property(nonatomic, assign) BOOL fileEnabled; @property(nonatomic, assign) FBLogLevel level; @property(nonatomic, copy, nullable) NSString* fileName; - (instancetype)init; @end ``` ## Engine Related ### Process Mode Image processing mode enumeration. * `FBProcessModeImage`: Image mode, suitable for single image processing * `FBProcessModeVideo`: Video mode, suitable for video streams and live streaming scenarios, better performance ```objc theme={null} typedef NS_ENUM(NSInteger, FBProcessMode) { FBProcessModeImage = 0, // Image mode FBProcessModeVideo = 1 // Video mode }; ``` ### Engine Configuration Engine configuration class for initializing the beauty engine. **Field Descriptions:** * `appId`: Application ID (optional, not required if `licenseJson` is provided) * `appKey`: Application key (optional, not required if `licenseJson` is provided) * `licenseJson`: License data JSON string (optional, if provided, takes priority and `appId` and `appKey` are not required) * `externalContext`: Whether to use external OpenGL context (default `NO`) * `YES`: Use the caller-provided GL context, SDK will not create/manage internal context * `NO`: Use internal default context **Verification Priority:** * If `licenseJson` is not empty, use license data verification (supports online response and offline license) * Otherwise, use `appId` and `appKey` for automatic online verification ```objc theme={null} FB_OBJC_API @interface FBEngineConfig : NSObject @property(nonatomic, copy) NSString* appId; @property(nonatomic, copy) NSString* appKey; @property(nonatomic, copy, nullable) NSString* licenseJson; // License data JSON string (optional) @property(nonatomic, assign) BOOL externalContext; // Whether to use external OpenGL context - (instancetype)init; @end ``` ### Engine Interface Main beauty effect engine class providing entry point for beauty functionality. **Static Methods:** * `setLogConfig:`: Set log configuration **Instance Methods:** #### Beauty Type Control (Deprecated) **Deprecated** The following APIs are deprecated. Please use parameter-driven APIs like `setBasicParam`, `setVirtualBackground`, `setFilter`, and `setSticker` instead. * `setBeautyTypeEnabled:enabled:`: \[Deprecated] Enable or disable beauty type (No effect in parameter-driven mode) * `isBeautyTypeEnabled:`: \[Deprecated] Check if beauty type is enabled (Always returns NO) * `disableAllBeautyTypes`: \[Deprecated] Disable all beauty types (Please reset effects by zeroing parameters) #### Parameter Settings * `setBasicParam:floatValue:`: Set basic beauty parameters (range 0.0 - 1.0) * `setReshapeParam:floatValue:`: Set face reshape parameters (range 0.0 - 1.0) * `setMakeupParam:floatValue:`: Set makeup parameters (range 0.0 - 1.0) * `setLipstickStyle:`: Set lipstick style (Rouge / Coral / Pink) * `setBlushStyle:`: Set blush style (Classic / Peach / Rose) * `setSkinOnlyBeauty:`: Set whether beauty is applied only to skin regions * Parameter: `enabled` - `YES` to enable skin-only beauty, `NO` to apply to entire image * `setVirtualBackground:`: Set virtual background * Parameter: `FBVirtualBackgroundOptions` object containing background mode and background image #### Filters & Stickers Management * `setFilter:`: Set the current filter ID (e.g., `"chuxin"`). Pass an empty string or `nil` to clear the filter. * `setFilterIntensity:`: Set the intensity of the current filter (range 0.0 - 1.0). * `setSticker:`: Set the current sticker ID. Pass an empty string or `nil` to clear the sticker. * `registerFilter:fbdFilePath:`: Register a filter resource from a file path. * `registerFilter:fbdData:`: Register a filter resource from memory data. * `registerSticker:fbdFilePath:`: Register a sticker resource from a file path. * `registerSticker:fbdData:`: Register a sticker resource from memory data. * `unregisterFilter:`: Unregister a specific filter and release its resources. * `unregisterAllFilters`: Unregister all filters. * `unregisterSticker:`: Unregister a specific sticker and release its resources. * `unregisterAllStickers`: Unregister all stickers. * `getRegisteredFilters`: Get the list of registered filter IDs. * `getRegisteredStickers`: Get the list of registered sticker IDs. #### Image Processing * `processImage:`: Process image frame * Frame type is obtained from `imageFrame.type` property (`FBFrameTypeImage` or `FBFrameTypeVideo`) * The processed image frame will maintain the same frame type **Return Value Descriptions:** * Methods return `int` type: `0` indicates success, other values indicate error codes * `processImage:` returns `FBImageFrame`: Processed image frame, returns `nil` on failure ```objc theme={null} FB_OBJC_API @interface FBBeautyEffectEngine : NSObject + (int)setLogConfig:(FBLogConfig*)config; + (instancetype)createEngineWithConfig:(FBEngineConfig*)config; - (int)setBasicParam:(FBBasicParam)param floatValue:(float)value; - (int)setReshapeParam:(FBReshapeParam)param floatValue:(float)value; - (int)setMakeupParam:(FBMakeupParam)param floatValue:(float)value; - (int)setLipstickStyle:(FBLipstickStyle)style; - (int)setBlushStyle:(FBBlushStyle)style; - (int)setSkinOnlyBeauty:(BOOL)enabled; - (int)setVirtualBackground:(FBVirtualBackgroundOptions*)options; - (int)setFilter:(NSString *_Nullable)filterId; - (int)setFilterIntensity:(float)intensity; - (int)setSticker:(NSString *_Nullable)stickerId; - (int)registerFilter:(NSString *)filterId fbdFilePath:(NSString *)fbdFilePath; - (int)registerFilter:(NSString *)filterId fbdData:(NSData *)fbdData; - (int)registerSticker:(NSString *)stickerId fbdFilePath:(NSString *)fbdFilePath; - (int)registerSticker:(NSString *)stickerId fbdData:(NSData *)fbdData; - (int)unregisterFilter:(NSString *)filterId; - (int)unregisterAllFilters; - (int)unregisterSticker:(NSString *)stickerId; - (int)unregisterAllStickers; - (NSArray *)getRegisteredFilters; - (NSArray *)getRegisteredStickers; - (FBImageFrame* _Nullable)processImage:(FBImageFrame*)imageFrame; // Deprecated APIs - (int)setBeautyTypeEnabled:(FBBeautyType)type enabled:(BOOL)enabled; - (BOOL)isBeautyTypeEnabled:(FBBeautyType)type; - (int)disableAllBeautyTypes; @end ``` ## Beauty Parameters Beauty parameter enumeration classes containing all beauty-related parameter type definitions. ### Beauty Types Define available beauty functionality types. ```objc theme={null} typedef NS_ENUM(NSInteger, FBBeautyType) { FBBeautyType_Basic = 0, // Basic beauty FBBeautyType_Reshape, // Face reshape FBBeautyType_Makeup, // Makeup effects FBBeautyType_VirtualBackground, // Virtual background }; ``` ### Basic Beauty Parameters Basic beauty effect parameter types, all parameter value ranges are `0.0 - 1.0`. ```objc theme={null} typedef NS_ENUM(NSInteger, FBBasicParam) { FBBasicParam_Smoothing = 0, // Smoothing FBBasicParam_Sharpening, // Sharpening FBBasicParam_Whitening, // Whitening FBBasicParam_Rosiness, // Rosiness }; ``` ### Face Reshape Parameters Face reshape effect parameter types, all parameter value ranges are `0.0 - 1.0`. ```objc theme={null} typedef NS_ENUM(NSInteger, FBReshapeParam) { FBReshapeParam_FaceThin = 0, // Face thinning FBReshapeParam_FaceVShape, // V-shaped face FBReshapeParam_FaceNarrow, // Narrow face FBReshapeParam_FaceShort, // Short face FBReshapeParam_Cheekbone, // Cheekbone FBReshapeParam_Jawbone, // Jawbone FBReshapeParam_Chin, // Chin FBReshapeParam_NoseSlim, // Nose slimming FBReshapeParam_EyeSize, // Eye enlargement FBReshapeParam_EyeDistance, // Eye distance }; ``` ### Makeup Parameters Makeup effect parameter types, all parameter value ranges are `0.0 - 1.0`. ```objc theme={null} typedef NS_ENUM(NSInteger, FBMakeupParam) { FBMakeupParam_Lipstick = 0, // Lipstick FBMakeupParam_Blush, // Blush }; typedef NS_ENUM(NSInteger, FBLipstickStyle) { FBLipstickStyle_Rouge = 0, // Rose red FBLipstickStyle_Coral, // Coral FBLipstickStyle_Pink, // Pink (default) }; typedef NS_ENUM(NSInteger, FBBlushStyle) { FBBlushStyle_Classic = 0, // Classic (default) FBBlushStyle_Peach, // Peach FBBlushStyle_Rose, // Rose }; ``` ### Virtual Background Options Virtual background effect configuration options. ```objc theme={null} FB_OBJC_API @interface FBVirtualBackgroundOptions : NSObject @property(nonatomic, assign) FBBackgroundMode mode; @property(nonatomic, strong, nullable) FBImageFrame *backgroundImage; - (instancetype)init; - (instancetype)initWithMode:(FBBackgroundMode)mode; @end;; ``` ## Image Related ### FBImageFrame Image frame class for encapsulating image data and processing. **Creation Methods:** * `createWithFile:`: Create from file (supports PNG, JPG) * `createWithRGBA:width:height:stride:`: Create from RGBA data * `createWithBGRA:width:height:stride:`: Create from BGRA data * `createWithRGB:width:height:stride:`: Create from RGB data * `createWithBGR:width:height:stride:`: Create from BGR data * `createWithI420:...`: Create from I420 YUV data * `createWithNV12:...`: Create from NV12 YUV data * `createWithNV21:...`: Create from NV21 YUV data * `createWithTexture:width:height:stride:`: Create from GPU texture (external texture input) * `createWithNSImage:`: Create from NSImage (macOS only) **Image Operations:** * `rotate:`: Rotate image (returns 0 for success) * `mirror:`: Mirror image (returns 0 for success) * Parameter: `mode` mirror mode, can be "horizontal", "vertical", or "both" (case insensitive) * `setMirror:`: Set mirror mode for engine processing (avoids extra format conversion) * Parameter: `mode` mirror mode, can be "horizontal", "vertical", or "both" (case insensitive) * Note: This only sets the mirror flag, actual mirroring happens during engine processing **Format Conversion:** * `convert:`: Format conversion method, returns converted `FBImageFrame` * `toFile:quality:`: Save image to file (specify quality 0-100) * `toFile:`: Save image to file (use default quality 90) ```objc theme={null} FB_OBJC_API @interface FBImageFrame : NSObject + (FBImageFrame *_Nullable)createWithFile:(NSString *)filePath; + (FBImageFrame *_Nullable)createWithRGBA:(const uint8_t *)data width:(int)width height:(int)height stride:(int)stride; + (FBImageFrame *_Nullable)createWithBGRA:(const uint8_t *)data width:(int)width height:(int)height stride:(int)stride; + (FBImageFrame *_Nullable)createWithRGB:(const uint8_t *)data width:(int)width height:(int)height stride:(int)stride; + (FBImageFrame *_Nullable)createWithBGR:(const uint8_t *)data width:(int)width height:(int)height stride:(int)stride; + (FBImageFrame *_Nullable)createWithI420:(int)width height:(int)height dataY:(const uint8_t *)dataY strideY:(int)strideY dataU:(const uint8_t *)dataU strideU:(int)strideU dataV:(const uint8_t *)dataV strideV:(int)strideV; + (FBImageFrame *_Nullable)createWithNV12:(int)width height:(int)height dataY:(const uint8_t *)dataY strideY:(int)strideY dataUV:(const uint8_t *)dataUV strideUV:(int)strideUV; + (FBImageFrame *_Nullable)createWithNV21:(int)width height:(int)height dataY:(const uint8_t *)dataY strideY:(int)strideY dataUV:(const uint8_t *)dataUV strideUV:(int)strideUV; + (FBImageFrame *_Nullable)createWithTexture:(uint32_t)texture width:(int)width height:(int)height stride:(int)stride; + (FBImageFrame *_Nullable)createWithNSImage:(NSImage *)image; - (int)rotate:(FBImageRotation)rotation; - (int)mirror:(NSString *)mode; - (void)setMirror:(NSString *)mode; - (FBImageFrame *_Nullable)convert:(FBImageFormat)format; - (int)toFile:(NSString *)filePath quality:(int)quality; - (int)toFile:(NSString *)filePath; // Property access methods - (int32_t)width; - (int32_t)height; - (int32_t)stride; - (int32_t)size; - (const uint8_t *_Nullable)data; - (FBImageFormat)format; - (uint32_t)texture; @property(nonatomic, assign) FBFrameType type; // YUV related methods - (const uint8_t *_Nullable)dataY; - (const uint8_t *_Nullable)dataU; - (const uint8_t *_Nullable)dataV; - (const uint8_t *_Nullable)dataUV; - (int32_t)strideY; - (int32_t)strideU; - (int32_t)strideV; - (int32_t)strideUV; @end ``` ### Enum Types **Supported Image Formats:** * `FBImageFormatI420`: YUV 4:2:0 (3 planes, Y, U, V) * `FBImageFormatNV12`: YUV 4:2:0 (2 planes, Y + UV) * `FBImageFormatNV21`: YUV 4:2:0 (2 planes, Y + VU) * `FBImageFormatRGBA`, `FBImageFormatBGRA`: 32-bit RGBA/BGRA * `FBImageFormatRGB`, `FBImageFormatBGR`: 24-bit RGB/BGR * `FBImageFormatTexture`: Texture format **Image Rotation Angles:** * `FBImageRotation0`: 0 degrees * `FBImageRotation90`: Clockwise 90 degrees * `FBImageRotation180`: Clockwise 180 degrees * `FBImageRotation270`: Clockwise 270 degrees **Property Access:** * `width`, `height`: Get image width and height * `size`: Get image data size (in bytes) * `stride`: Get image stride **Data Access:** * `data`: Get raw image data pointer * YUV format specific: `dataY`, `dataU`, `dataV`, `dataUV` * YUV format strides: `strideY`, `strideU`, `strideV`, `strideUV` ```objc theme={null} typedef NS_ENUM(NSInteger, FBImageFormat) { FBImageFormatI420, FBImageFormatNV12, FBImageFormatNV21, FBImageFormatBGRA, FBImageFormatRGBA, FBImageFormatBGR, FBImageFormatRGB, FBImageFormatTexture, }; typedef NS_ENUM(NSInteger, FBImageRotation) { FBImageRotation0, // 0 degrees FBImageRotation90, // Clockwise 90 degrees FBImageRotation180, // Clockwise 180 degrees FBImageRotation270, // Clockwise 270 degrees }; typedef NS_ENUM(NSInteger, FBFrameType) { FBFrameTypeImage = 0, // Image mode FBFrameTypeVideo = 1 // Video mode }; ``` # Best Practices Source: https://facebetter.mintlify.app/macos/best-practices macOS Beauty SDK Best Practices Guide ## Performance Optimization ### 1. Choose the Right Processing Mode **Video Mode (FBProcessModeVideo)** * Suitable for real-time video streams and live streaming scenarios * Better performance and faster processing speed * Recommended for camera preview, video calls, and similar scenarios **Image Mode (FBProcessModeImage)** * Suitable for single image processing * Higher quality and better effects * Recommended for photo editing and image beautification scenarios ```objc theme={null} // Real-time video processing FBImageFrame *output = [engine processImage:input processMode:FBProcessModeVideo]; // High-quality image processing FBImageFrame *output = [engine processImage:input processMode:FBProcessModeImage]; ``` ### 2. Parameter Adjustment Recommendations **Beauty Parameter Adjustment Principles** * Start with smaller values to avoid over-beautification * Recommend reducing parameter values in real-time scenarios for smooth performance * Static images can have appropriately higher parameter values * Adjust parameter ranges based on user groups and scenarios **Recommended Parameter Ranges** ```objc theme={null} // Basic beauty parameters (real-time scenarios) [self.beautyEngine setBasicParam:FBBasicParam_Smoothing floatValue:0.2f]; // Smoothing [self.beautyEngine setBasicParam:FBBasicParam_Whitening floatValue:0.1f]; // Whitening [self.beautyEngine setBasicParam:FBBasicParam_Rosiness floatValue:0.1f]; // Rosiness // Face reshaping parameters (real-time scenarios) [self.beautyEngine setReshapeParam:FBReshapeParam_FaceThin floatValue:0.1f]; // Face thinning [self.beautyEngine setReshapeParam:FBReshapeParam_EyeSize floatValue:0.1f]; // Eye enlargement ``` ### 3. Memory Optimization **Use ARC for Memory Management** ```objc theme={null} // Recommended: Use ARC automatic management @property (nonatomic, strong) FBImageFrame *reusableFrame; // Avoid: Manual memory management (unless necessary) // FBImageFrame *frame = [[FBImageFrame alloc] init]; // [frame release]; ``` **Release Resources Timely** ```objc theme={null} @interface BeautyProcessor : NSObject @property (nonatomic, strong) FBImageFrame *reusableFrame; // Reusable object @end @implementation BeautyProcessor - (FBImageFrame *)processImage:(NSData *)imageData { // Reuse FBImageFrame object if (self.reusableFrame == nil) { self.reusableFrame = [FBImageFrame createWithRGBA:data width:width height:height stride:stride]; } // Process image FBImageFrame *output = [self.beautyEngine processImage:self.reusableFrame processMode:FBProcessModeVideo]; return output; } - (void)release { self.reusableFrame = nil; // ARC will automatically release } @end ``` ## Architecture Design ### 1. Singleton Pattern for Engine Management ```objc theme={null} @interface BeautyEngineManager : NSObject @property (nonatomic, strong, readonly) FBBeautyEffectEngine *engine; + (instancetype)sharedManager; - (void)releaseEngine; @end @implementation BeautyEngineManager + (instancetype)sharedManager { static BeautyEngineManager *instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[BeautyEngineManager alloc] init]; }); return instance; } - (instancetype)init { self = [super init]; if (self) { [self initEngine]; } return self; } - (void)initEngine { FBEngineConfig *config = [[FBEngineConfig alloc] init]; config.appId = @"your_app_id"; config.appKey = @"your_app_key"; _engine = [FBBeautyEffectEngine createEngineWithConfig:config]; } - (void)releaseEngine { _engine = nil; } @end ``` ### 2. Asynchronous Image Processing ```objc theme={null} @interface AsyncBeautyProcessor : NSObject @property (nonatomic, strong) dispatch_queue_t processingQueue; @property (nonatomic, strong) FBBeautyEffectEngine *engine; @end @implementation AsyncBeautyProcessor - (instancetype)init { self = [super init]; if (self) { self.processingQueue = dispatch_queue_create("com.facebetter.processing", DISPATCH_QUEUE_SERIAL); self.engine = [BeautyEngineManager sharedManager].engine; } return self; } - (void)processImageAsync:(FBImageFrame *)input completion:(void(^)(FBImageFrame *result, NSError *error))completion { dispatch_async(self.processingQueue, ^{ @try { FBImageFrame *output = [self.engine processImage:input processMode:FBProcessModeVideo]; // Switch to main thread for callback dispatch_async(dispatch_get_main_queue(), ^{ completion(output, nil); }); } @catch (NSException *exception) { NSError *error = [NSError errorWithDomain:@"FacebetterError" code:-1 userInfo:@{NSLocalizedDescriptionKey: exception.reason}]; dispatch_async(dispatch_get_main_queue(), ^{ completion(nil, error); }); } }); } @end ``` ## Error Handling ### 1. Comprehensive Error Handling Mechanism ```objc theme={null} @interface RobustBeautyProcessor : NSObject @property (nonatomic, strong) FBBeautyEffectEngine *engine; @end @implementation RobustBeautyProcessor - (BOOL)processImage:(FBImageFrame *)input output:(FBImageFrame *)output { // Parameter validation if (self.engine == nil) { NSLog(@"Engine not initialized"); return NO; } if (input == nil) { NSLog(@"Invalid input image"); return NO; } @try { // Process image FBImageFrame *result = [self.engine processImage:input processMode:FBProcessModeVideo]; if (result == nil) { NSLog(@"Failed to process image"); return NO; } // Copy result to output [self copyImageFrame:result to:output]; return YES; } @catch (NSException *exception) { NSLog(@"Exception during processing: %@", exception.reason); return NO; } } - (void)copyImageFrame:(FBImageFrame *)src to:(FBImageFrame *)dst { // Implement image copying logic } @end ``` ### 2. Retry Mechanism ```objc theme={null} @interface RetryBeautyProcessor : NSObject @property (nonatomic, strong) FBBeautyEffectEngine *engine; @end @implementation RetryBeautyProcessor static const int MAX_RETRY_COUNT = 3; static const NSTimeInterval RETRY_DELAY = 0.1; - (FBImageFrame *)processImageWithRetry:(FBImageFrame *)input { for (int i = 0; i < MAX_RETRY_COUNT; i++) { @try { FBImageFrame *result = [self.engine processImage:input processMode:FBProcessModeVideo]; if (result != nil) { return result; } } @catch (NSException *exception) { NSLog(@"Attempt %d failed: %@", i + 1, exception.reason); } if (i < MAX_RETRY_COUNT - 1) { [NSThread sleepForTimeInterval:RETRY_DELAY]; } } NSLog(@"All retry attempts failed"); return nil; } @end ``` ## Lifecycle Management The beauty engine is a singleton and is automatically released when the app ends. Manual management is not required. However, stopping beauty processing when leaving a page/ViewController can save resources. ### 1. NSViewController Lifecycle Handling ```objc theme={null} @interface BeautyViewController : NSViewController @property (nonatomic, strong) BeautyEngineManager *beautyManager; @end @implementation BeautyViewController - (void)viewDidLoad { [super viewDidLoad]; // Initialize beauty engine self.beautyManager = [BeautyEngineManager sharedManager]; } - (void)viewWillAppear { [super viewWillAppear]; // Resume beauty processing } - (void)viewWillDisappear { [super viewWillDisappear]; // Pause beauty processing } - (void)dealloc { // Release beauty engine (if no longer needed) // [self.beautyManager releaseEngine]; } @end ``` ### 2. Application Lifecycle Handling ```objc theme={null} - (void)applicationDidFinishLaunching:(NSNotification *)notification { // Initialize beauty engine when app launches [[BeautyEngineManager sharedManager] initEngine]; } - (void)applicationWillTerminate:(NSNotification *)notification { // Release beauty engine when app terminates [[BeautyEngineManager sharedManager] releaseEngine]; } - (void)applicationDidResignActive:(NSNotification *)notification { // Pause beauty processing when app loses focus [[BeautyEngineManager sharedManager] pauseProcessing]; } - (void)applicationDidBecomeActive:(NSNotification *)notification { // Resume beauty processing when app gains focus [[BeautyEngineManager sharedManager] resumeProcessing]; } ``` ## Performance Monitoring ### 1. Performance Metrics Monitoring ```objc theme={null} @interface BeautyPerformanceMonitor : NSObject @property (nonatomic, assign) NSTimeInterval startTime; @property (nonatomic, assign) NSInteger frameCount; @property (nonatomic, assign) NSTimeInterval totalProcessTime; @end @implementation BeautyPerformanceMonitor - (void)startFrame { self.startTime = [[NSDate date] timeIntervalSince1970]; } - (void)endFrame { NSTimeInterval processTime = [[NSDate date] timeIntervalSince1970] - self.startTime; self.totalProcessTime += processTime; self.frameCount++; // Calculate average processing time if (self.frameCount % 30 == 0) { // Calculate every 30 frames NSTimeInterval avgTime = self.totalProcessTime / self.frameCount; NSLog(@"Average process time: %.2fms", avgTime * 1000); // Reset counters self.totalProcessTime = 0; self.frameCount = 0; } } - (BOOL)isPerformanceGood { return (self.totalProcessTime / MAX(self.frameCount, 1)) < 0.033; // 30fps } @end ``` ### 2. Memory Usage Monitoring ```objc theme={null} @interface MemoryMonitor : NSObject @end @implementation MemoryMonitor - (void)logMemoryUsage:(NSString *)tag { struct mach_task_basic_info info; mach_msg_type_number_t size = MACH_TASK_BASIC_INFO_COUNT; kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size); if (kerr == KERN_SUCCESS) { NSLog(@"%@ - Used: %.2fMB, Resident: %.2fMB", tag, info.resident_size / 1024.0 / 1024.0, info.resident_size / 1024.0 / 1024.0); } } - (BOOL)isMemoryLow { struct mach_task_basic_info info; mach_msg_type_number_t size = MACH_TASK_BASIC_INFO_COUNT; kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size); if (kerr == KERN_SUCCESS) { // macOS has more memory, threshold can be set higher return info.resident_size > 500 * 1024 * 1024; // 500MB } return NO; } @end ``` ## Configuration Management ### 1. Beauty Configuration Management ```objc theme={null} @interface BeautyConfigManager : NSObject @property (nonatomic, strong) NSUserDefaults *userDefaults; @end @implementation BeautyConfigManager - (instancetype)init { self = [super init]; if (self) { self.userDefaults = [NSUserDefaults standardUserDefaults]; } return self; } - (void)saveBeautyConfig:(BeautyConfig *)config { NSDictionary *configDict = @{ @"smoothing": @(config.smoothing), @"whitening": @(config.whitening), @"face_thin": @(config.faceThin), @"basic_enabled": @(config.basicEnabled), @"reshape_enabled": @(config.reshapeEnabled) }; [self.userDefaults setObject:configDict forKey:@"beauty_config"]; [self.userDefaults synchronize]; } - (BeautyConfig *)loadBeautyConfig { BeautyConfig *config = [[BeautyConfig alloc] init]; NSDictionary *configDict = [self.userDefaults objectForKey:@"beauty_config"]; if (configDict) { config.smoothing = [configDict[@"smoothing"] floatValue]; config.whitening = [configDict[@"whitening"] floatValue]; config.faceThin = [configDict[@"face_thin"] floatValue]; config.basicEnabled = [configDict[@"basic_enabled"] boolValue]; config.reshapeEnabled = [configDict[@"reshape_enabled"] boolValue]; } return config; } @end @interface BeautyConfig : NSObject @property (nonatomic, assign) float smoothing; @property (nonatomic, assign) float whitening; @property (nonatomic, assign) float faceThin; @property (nonatomic, assign) BOOL basicEnabled; @property (nonatomic, assign) BOOL reshapeEnabled; @end @implementation BeautyConfig - (instancetype)init { self = [super init]; if (self) { self.smoothing = 0.3f; self.whitening = 0.2f; self.faceThin = 0.1f; self.basicEnabled = YES; self.reshapeEnabled = NO; } return self; } @end ``` ## macOS-Specific Optimizations ### 1. Multi-Display Support ```objc theme={null} @interface MultiDisplayManager : NSObject @property (nonatomic, strong) NSArray *screens; @end @implementation MultiDisplayManager - (void)setupMultiDisplaySupport { self.screens = [NSScreen screens]; for (NSScreen *screen in self.screens) { CGFloat scaleFactor = screen.backingScaleFactor; if (scaleFactor > 1.0) { // Retina display optimization NSLog(@"Retina display detected with scale factor: %.1f", scaleFactor); // Adjust processing parameters based on scale factor } } } - (void)handleDisplayChange:(NSNotification *)notification { // Reconfigure when display configuration changes [self setupMultiDisplaySupport]; } @end ``` ### 2. Window Management ```objc theme={null} @interface WindowManager : NSObject @property (nonatomic, weak) NSWindow *mainWindow; @end @implementation WindowManager - (void)handleWindowStateChange:(NSNotification *)notification { NSWindow *window = notification.object; if (window.isMiniaturized) { // Pause processing when window is minimized [[BeautyEngineManager sharedManager] pauseProcessing]; NSLog(@"Window minimized, pausing beauty processing"); } else if (window.isVisible) { // Resume processing when window is visible [[BeautyEngineManager sharedManager] resumeProcessing]; NSLog(@"Window visible, resuming beauty processing"); } } - (void)handleWindowDidResize:(NSNotification *)notification { NSWindow *window = notification.object; NSSize newSize = window.frame.size; // Adjust processing parameters when window size changes NSLog(@"Window resized to: %.0fx%.0f", newSize.width, newSize.height); // Adjust beauty parameters based on new size } @end ``` ### 3. System Performance Monitoring ```objc theme={null} @interface SystemPerformanceMonitor : NSObject @end @implementation SystemPerformanceMonitor - (void)monitorSystemPerformance { NSProcessInfo *processInfo = [NSProcessInfo processInfo]; NSUInteger processorCount = processInfo.processorCount; NSUInteger activeProcessorCount = processInfo.activeProcessorCount; float systemLoad = (float)activeProcessorCount / processorCount; if (systemLoad < 0.3) { // Low system load, can use high quality mode NSLog(@"System load low (%.1f%%), using high quality mode", systemLoad * 100); } else if (systemLoad < 0.7) { // Medium system load, use medium quality mode NSLog(@"System load medium (%.1f%%), using medium quality mode", systemLoad * 100); } else { // High system load, use low quality mode NSLog(@"System load high (%.1f%%), using low quality mode", systemLoad * 100); } } @end ``` ## Testing Recommendations ### 1. Unit Testing ```objc theme={null} @interface BeautyEffectTests : XCTestCase @property (nonatomic, strong) FBBeautyEffectEngine *beautyEngine; @end @implementation BeautyEffectTests - (void)setUp { [super setUp]; FBEngineConfig *config = [[FBEngineConfig alloc] init]; config.appId = @"test_app_id"; config.appKey = @"test_app_key"; self.beautyEngine = [FBBeautyEffectEngine createEngineWithConfig:config]; } - (void)testBeautyEngineInitialization { XCTAssertNotNil(self.beautyEngine, @"Engine should be created"); } - (void)testImageProcessing { // Create test image uint8_t *data = malloc(640 * 480 * 4); FBImageFrame *input = [FBImageFrame createWithRGBA:data width:640 height:480 stride:640 * 4]; // Process image FBImageFrame *output = [self.beautyEngine processImage:input processMode:FBProcessModeVideo]; XCTAssertNotNil(output, @"Output should not be null"); free(data); } - (void)tearDown { self.beautyEngine = nil; [super tearDown]; } @end ``` ### 2. Performance Testing ```objc theme={null} - (void)testPerformance { [self measureBlock:^{ NSTimeInterval startTime = [[NSDate date] timeIntervalSince1970]; for (int i = 0; i < 100; i++) { // Process test image [self processTestImage]; } NSTimeInterval endTime = [[NSDate date] timeIntervalSince1970]; NSTimeInterval totalTime = endTime - startTime; NSTimeInterval avgTime = totalTime / 100; // macOS has better performance, threshold can be set lower XCTAssertLessThan(avgTime, 0.03, @"Average process time should be less than 30ms"); }]; } ``` ## Summary Following these best practices can help you: 1. **Improve Performance**: Through memory management, multi-threading, and image optimization 2. **Improve User Experience**: Through progressive loading and smooth transitions 3. **Improve Code Quality**: Through modular design and error handling 4. **Ensure Stability**: Through comprehensive testing strategies 5. **macOS Optimization**: Through multi-display support and window management Remember to adjust these practices based on specific needs and continuously monitor application performance. The macOS platform has more memory and stronger processing capabilities compared to iOS, allowing support for higher quality beauty effects. # Error Handling Source: https://facebetter.mintlify.app/macos/error-handling macOS Beauty SDK Error Handling Guide ## Error Code Handling ### 1. Check API Return Values All API calls should check return values: ```objc theme={null} // Check engine creation FBEngineConfig *config = [[FBEngineConfig alloc] init]; config.appId = @"your_app_id"; config.appKey = @"your_app_key"; self.engine = [FBBeautyEffectEngine createEngineWithConfig:config]; if (self.engine == nil) { NSLog(@"Failed to create beauty engine"); return; } // Check parameter setting int ret = [self.engine setBasicParam:FBBeautyParam_Smoothing floatValue:0.5f]; if (ret == 0) { NSLog(@"Successfully set basic beauty parameters"); } else { NSLog(@"Failed to set smoothing param, error code: %d", ret); } ``` ### 2. Common Error Codes According to API documentation, main error codes: * **0**: Success * **-1**: Engine not initialized ```objc theme={null} @interface BeautyErrorHandler : NSObject @end @implementation BeautyErrorHandler + (void)handleError:(int)errorCode operation:(NSString *)operation { switch (errorCode) { case 0: NSLog(@"%@ succeeded", operation); break; case -1: NSLog(@"%@ failed: Engine not initialized", operation); break; default: NSLog(@"%@ failed: Unknown error code %d", operation, errorCode); break; } } @end ``` ## Handle Image Data ### 1. Input Image Validation ```objc theme={null} @interface ImageValidator : NSObject + (BOOL)validateImageFrame:(FBImageFrame *)imageFrame; @end @implementation ImageValidator + (BOOL)validateImageFrame:(FBImageFrame *)imageFrame { if (imageFrame == nil) { NSLog(@"ImageFrame is nil"); return NO; } // Check image dimensions int width = imageFrame.width; int height = imageFrame.height; if (width <= 0 || height <= 0) { NSLog(@"Invalid image dimensions: %dx%d", width, height); return NO; } // macOS can support larger images if (width > 8192 || height > 8192) { NSLog(@"Image too large: %dx%d", width, height); return NO; } return YES; } @end ``` ### 2. Image Processing Error Handling ```objc theme={null} @interface ImageProcessor : NSObject @property (nonatomic, strong) FBBeautyEffectEngine *engine; @end @implementation ImageProcessor - (FBImageFrame *)processImageSafely:(FBImageFrame *)input { // Validate input if (![ImageValidator validateImageFrame:input]) { return nil; } @try { // Process image input.type = FBFrameTypeVideo; FBImageFrame *output = [self.engine processImage:input]; if (output == nil) { NSLog(@"Failed to process image: output is nil"); return nil; } return output; } @catch (NSException *exception) { NSLog(@"Exception during image processing: %@", exception.reason); return nil; } } @end ``` ### 3. Memory Management ```objc theme={null} @interface SafeImageProcessor : NSObject @property (nonatomic, strong) FBBeautyEffectEngine *engine; @end @implementation SafeImageProcessor - (BOOL)processImageSafe:(FBImageFrame *)input output:(FBImageFrame *)output { FBImageFrame *result = nil; @try { // Process image result = [self.engine processImage:input processMode:FBProcessModeVideo]; if (result == nil) { NSLog(@"Processing returned nil"); return NO; } // Copy result to output [self copyImageFrame:result to:output]; return YES; } @catch (NSException *exception) { NSLog(@"Exception during processing: %@", exception.reason); return NO; } } - (void)copyImageFrame:(FBImageFrame *)src to:(FBImageFrame *)dst { // Implement image copying logic FBImageBuffer *srcBuffer = [src toRGBA]; FBImageBuffer *dstBuffer = [dst toRGBA]; if (srcBuffer != nil && dstBuffer != nil) { const uint8_t *srcData = [srcBuffer data]; uint8_t *dstData = (uint8_t *)[dstBuffer data]; if (srcData != nil && dstData != nil) { memcpy(dstData, srcData, MIN(srcBuffer.size, dstBuffer.size)); } } } @end ``` ## Logging and File Operations ### 1. Log Configuration Error Handling ```objc theme={null} @interface LogConfigHandler : NSObject + (BOOL)configureLogging:(BOOL)enableConsole enableFile:(BOOL)enableFile logFilePath:(NSString *)logFilePath; @end @implementation LogConfigHandler + (BOOL)configureLogging:(BOOL)enableConsole enableFile:(BOOL)enableFile logFilePath:(NSString *)logFilePath { @try { FBLogConfig *logConfig = [[FBLogConfig alloc] init]; logConfig.consoleEnabled = enableConsole; logConfig.fileEnabled = enableFile; logConfig.level = FBLogLevel_Info; if (enableFile && logFilePath != nil && logFilePath.length > 0) { // Check if directory exists NSString *logDir = [logFilePath stringByDeletingLastPathComponent]; NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:logDir]) { NSError *error = nil; if (![fileManager createDirectoryAtPath:logDir withIntermediateDirectories:YES attributes:nil error:&error]) { NSLog(@"Failed to create log directory: %@", error.localizedDescription); return NO; } } logConfig.fileName = logFilePath; } int ret = [FBBeautyEffectEngine setLogConfig:logConfig]; return ret == 0; } @catch (NSException *exception) { NSLog(@"Failed to configure logging: %@", exception.reason); return NO; } } @end ``` ### 2. File Write Error Handling ```objc theme={null} @interface FileWriteHandler : NSObject + (BOOL)saveImageBuffer:(FBImageBuffer *)buffer toPath:(NSString *)filePath; @end @implementation FileWriteHandler + (BOOL)saveImageBuffer:(FBImageBuffer *)buffer toPath:(NSString *)filePath { if (buffer == nil) { NSLog(@"ImageBuffer is nil"); return NO; } NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *dir = [filePath stringByDeletingLastPathComponent]; // Create directory if (![fileManager fileExistsAtPath:dir]) { NSError *error = nil; if (![fileManager createDirectoryAtPath:dir withIntermediateDirectories:YES attributes:nil error:&error]) { NSLog(@"Failed to create directory: %@", error.localizedDescription); return NO; } } // Write file const uint8_t *data = [buffer data]; NSData *nsData = [NSData dataWithBytes:data length:buffer.size]; NSError *error = nil; BOOL success = [nsData writeToFile:filePath options:NSDataWritingAtomic error:&error]; if (!success) { NSLog(@"Failed to write file: %@", error.localizedDescription); return NO; } NSLog(@"Image saved successfully: %@", filePath); return YES; } @end ``` ## Exception Handling ### 1. Catch and Handle Exceptions ```objc theme={null} @interface ExceptionHandler : NSObject + (void)safeExecute:(void(^)(void))block operation:(NSString *)operation; @end @implementation ExceptionHandler + (void)safeExecute:(void(^)(void))block operation:(NSString *)operation { @try { if (block != nil) { block(); } } @catch (NSInvalidArgumentException *exception) { NSLog(@"%@ failed: Invalid argument - %@", operation, exception.reason); } @catch (NSException *exception) { NSLog(@"%@ failed: Unexpected exception - %@", operation, exception.reason); } } @end ``` ### 2. Retry Mechanism ```objc theme={null} @interface RetryHandler : NSObject @property (nonatomic, strong) FBBeautyEffectEngine *engine; @end @implementation RetryHandler static const int MAX_RETRY = 3; static const NSTimeInterval RETRY_DELAY = 0.1; - (FBImageFrame *)processImageWithRetry:(FBImageFrame *)input { for (int i = 0; i < MAX_RETRY; i++) { @try { FBImageFrame *result = [self.engine processImage:input processMode:FBProcessModeVideo]; if (result != nil) { return result; } } @catch (NSException *exception) { NSLog(@"Attempt %d failed: %@", i + 1, exception.reason); } if (i < MAX_RETRY - 1) { [NSThread sleepForTimeInterval:RETRY_DELAY]; } } NSLog(@"All retry attempts failed"); return nil; } @end ``` ## macOS-Specific Error Handling ### 1. Sandbox Permission Handling ```objc theme={null} @interface SandboxPermissionHandler : NSObject + (BOOL)requestFileAccessPermission:(NSURL *)fileURL; + (BOOL)hasFileAccessPermission:(NSURL *)fileURL; @end @implementation SandboxPermissionHandler + (BOOL)requestFileAccessPermission:(NSURL *)fileURL { // macOS sandbox environment requires file access permission request NSError *error = nil; BOOL success = [fileURL startAccessingSecurityScopedResource]; if (!success) { NSLog(@"Failed to request file access permission: %@", error.localizedDescription); } return success; } + (BOOL)hasFileAccessPermission:(NSURL *)fileURL { // Check if file access permission exists NSFileManager *fileManager = [NSFileManager defaultManager]; return [fileManager isReadableFileAtPath:fileURL.path]; } @end ``` ### 2. Multi-Display Environment Error Handling ```objc theme={null} @interface MultiDisplayErrorHandler : NSObject + (void)handleDisplayChange:(NSNotification *)notification; + (BOOL)validateDisplayConfiguration; @end @implementation MultiDisplayErrorHandler + (void)handleDisplayChange:(NSNotification *)notification { // Handle display configuration changes NSArray *screens = [NSScreen screens]; for (NSScreen *screen in screens) { CGFloat scaleFactor = screen.backingScaleFactor; if (scaleFactor > 1.0) { // Retina display NSLog(@"Retina display detected with scale factor: %.1f", scaleFactor); } } // Revalidate configuration if (![self validateDisplayConfiguration]) { NSLog(@"Display configuration validation failed"); } } + (BOOL)validateDisplayConfiguration { NSArray *screens = [NSScreen screens]; if (screens.count == 0) { NSLog(@"No displays detected"); return NO; } // Validate all display configurations for (NSScreen *screen in screens) { if (screen.frame.size.width <= 0 || screen.frame.size.height <= 0) { NSLog(@"Invalid screen dimensions"); return NO; } } return YES; } @end ``` ## Debugging Tips ### 1. Error Information Collection ```objc theme={null} @interface DebugInfoCollector : NSObject + (NSString *)collectErrorInfo:(int)errorCode operation:(NSString *)operation exception:(NSException *)exception; @end @implementation DebugInfoCollector + (NSString *)collectErrorInfo:(int)errorCode operation:(NSString *)operation exception:(NSException *)exception { NSMutableString *info = [NSMutableString string]; [info appendFormat:@"Operation: %@\n", operation]; [info appendFormat:@"Error Code: %d\n", errorCode]; [info appendFormat:@"Timestamp: %lld\n", (long long)([[NSDate date] timeIntervalSince1970] * 1000)]; if (exception != nil) { [info appendFormat:@"Exception: %@\n", exception.name]; [info appendFormat:@"Reason: %@\n", exception.reason]; } // Add system information [info appendString:@"System Info:\n"]; NSProcessInfo *processInfo = [NSProcessInfo processInfo]; [info appendFormat:@"Processor Count: %lu\n", (unsigned long)processInfo.processorCount]; [info appendFormat:@"Active Processor Count: %lu\n", (unsigned long)processInfo.activeProcessorCount]; return [info copy]; } @end ``` ### 2. Performance Monitoring ```objc theme={null} @interface PerformanceMonitor : NSObject @property (nonatomic, assign) NSTimeInterval startTime; @property (nonatomic, assign) NSInteger errorCount; @end @implementation PerformanceMonitor - (void)startOperation { self.startTime = [[NSDate date] timeIntervalSince1970]; } - (void)endOperation:(NSString *)operation success:(BOOL)success { NSTimeInterval duration = [[NSDate date] timeIntervalSince1970] - self.startTime; if (!success) { self.errorCount++; NSLog(@"%@ failed after %.2fms (Error count: %ld)", operation, duration * 1000, (long)self.errorCount); } else if (duration > 0.05) { // macOS has better performance, threshold can be set lower NSLog(@"%@ took %.2fms (slow operation)", operation, duration * 1000); } } @end ``` ## Summary Following these error handling best practices can help you: 1. **Improve App Stability**: Through API return value checking and exception handling 2. **Improve User Experience**: Through friendly error prompts and recovery mechanisms 3. **Facilitate Problem Troubleshooting**: Through detailed logging and error information collection 4. **Optimize Performance**: Through reasonable error recovery strategies and resource management 5. **macOS Optimization**: Through sandbox permission handling and multi-display environment support Remember to adjust error handling logic based on actual SDK error code definitions and continuously monitor and improve error handling mechanisms. The macOS platform has more memory and stronger processing capabilities compared to iOS, but also need to pay attention to special handling for sandbox permissions and multi-display environments. # FAQ Source: https://facebetter.mintlify.app/macos/faq macOS Beauty SDK Common Questions and Answers ## Integration Issues ### Q: What to do if engine creation fails? A: Check the following points: * Confirm that `appId` and `appKey` are correct * Check if network connection is normal * View log output for detailed error information * Confirm if SDK version is latest * Check if Framework is properly linked ### Q: Can't find header files or classes during compilation? A: Possible reasons: * Confirm that `Facebetter.framework` has been properly added to the project * Check if Framework's "Embed & Sign" setting is correct * Confirm import statement: `#import ` * Try Clean Build Folder and rebuild ### Q: Link errors at runtime? A: Solutions: * Confirm if device architecture is supported (x86\_64, arm64) * Check if Framework file is complete * Confirm Framework's deployment target setting is correct * Check for other Framework conflicts ## Functionality Issues ### Q: Beauty effects are not obvious? A: You can try: * Increase beauty parameter values (range 0.0-1.0) * Ensure corresponding beauty types are enabled * Check if image quality is clear enough * Confirm if face detection is working normally ### Q: Beauty effects are excessive or distorted? A: Recommendations: * Reduce beauty parameter values * Check if parameter combinations are reasonable * Avoid enabling too many beauty types simultaneously * Adjust parameters based on image quality ### Q: Virtual background not working? A: Check: * Confirm `FBBeautyType_VirtualBackground` is enabled * Check if background image path is correct * Confirm if image format is supported (PNG, JPG) * Check if image file exists and is readable ## Performance Issues ### Q: What to do if processing is slow? A: Optimization suggestions: * Use `FBFrameTypeVideo` for real-time processing * Reduce image resolution * Reduce number of simultaneously enabled beauty types * Avoid image processing on main thread * Use multi-threading for image processing * Leverage macOS multi-core advantages ### Q: High memory usage? A: Solutions: * Release `FBImageFrame` and `FBImageBuffer` objects timely * Avoid frequent creation and destruction of engine instances * Reuse `FBImageFrame` objects * Use object pool pattern to manage image buffers * Monitor system memory usage ### Q: App crashes or freezes? A: Troubleshooting steps: * Check if image processing is done on main thread * Confirm resource release is complete * View exception information in logs * Check if parameter values are within valid range * Use Instruments tool to analyze memory and performance * Check compatibility in multi-display environments ## Image Processing Issues ### Q: How to handle different image formats? A: Use `FBImageFrame`'s format conversion methods: * `toRGBA` - Convert to RGBA format * `toI420` - Convert to I420 format * `toNV12` - Convert to NV12 format * `toBGRA` - Convert to BGRA format ### Q: Camera preview image processing? A: Recommended process: 1. Get CVPixelBuffer from AVCaptureSession 2. Use `createWithData` to create FBImageFrame 3. Call `processImage` to process 4. Convert to target format for display ### Q: Image rotation issues? A: Solutions: * Use `FBImageFrame`'s `rotate` method to rotate images * Supports 0°, 90°, 180°, 270° rotation * Rotation operation modifies original image data ## Permission Issues ### Q: Camera permission issues? A: Need to add permission description: ```xml theme={null} NSCameraUsageDescription Camera permission required for beauty photography ``` ### Q: Microphone permission issues? A: Need to add permission description: ```xml theme={null} NSMicrophoneUsageDescription Microphone permission required for audio/video recording ``` ## Debugging Issues ### Q: How to enable debug logging? A: Configure before creating engine: ```objc theme={null} FBLogConfig *logConfig = [[FBLogConfig alloc] init]; logConfig.consoleEnabled = YES; logConfig.fileEnabled = YES; logConfig.level = FBLogLevel_Debug; [FBBeautyEffectEngine setLogConfig:logConfig]; ``` ### Q: How to get detailed error information? A: Methods: * Enable DEBUG level logging * Check API return values (0 indicates success) * View file log output * Use Xcode's Console to view logs * Use Console.app to view system logs ## Version Compatibility ### Q: Which macOS versions are supported? A: Supports macOS 10.14 and above ### Q: Which device architectures are supported? A: Supports: * x86\_64 (Intel Mac) * arm64 (Apple Silicon Mac) ### Q: How to upgrade SDK version? A: Steps: 1. Download new version SDK 2. Replace old Framework file 3. Clean project cache 4. Rebuild project 5. Test if functionality works normally ## ARC Related Issues ### Q: How to handle memory management? A: macOS uses ARC, but need to pay attention to: * Set objects to nil timely * Avoid circular references * Use weak references to avoid strong reference cycles ### Q: How to handle CVPixelBuffer? A: Recommendations: * Use `CVPixelBufferRetain` and `CVPixelBufferRelease` for management * Or use `__bridge_transfer` and `__bridge_retained` for conversion ## Multi-threading Issues ### Q: How to process images in background thread? A: Recommendations: * Use `dispatch_async` to process in background queue * Avoid image processing on main thread * Use `dispatch_async(dispatch_get_main_queue())` to update UI ### Q: How to handle concurrent access? A: Solutions: * Use `@synchronized` to protect shared resources * Use serial queue for image processing * Avoid multi-threaded access to engine instance ## macOS-Specific Issues ### Q: Multi-display support issues? A: Solutions: * Check resolution and scale factor of different displays * Adapt to Retina display high resolution * Handle window movement between displays ### Q: Window state management issues? A: Recommendations: * Listen for window minimize/restore events * Pause processing when window is not visible * Resume processing when window is visible again ### Q: System performance monitoring? A: Methods: * Use `NSProcessInfo` to monitor system load * Dynamically adjust processing quality based on system performance * Listen for system memory warning notifications ## Deployment Issues ### Q: Mac App Store review rejected? A: Possible reasons: * Missing necessary permission descriptions * Using private APIs * Memory leak issues * Crash issues * Sandbox permission configuration issues ### Q: How to optimize app size? A: Recommendations: * Remove unused architectures (like x86\_64) * Compress resource files * Use App Thinning * Remove unused Framework parts * Package separately for different architectures ### Q: How to support Universal Binary? A: Steps: 1. Ensure Framework contains both x86\_64 and arm64 architectures 2. Set "Architectures" to "Standard Architectures" in Xcode 3. Use `lipo` command to verify architecture support 4. Test on different architecture Macs # Implement Beauty Source: https://facebetter.mintlify.app/macos/implement-beauty Implement macOS Beauty ## Add SDK Dependency ### Method A: CocoaPods Integration (Recommended) Add the `Facebetter` dependency to your project's `Podfile`: ```ruby theme={null} target 'YourTargetName' do # Please replace with the latest version pod 'Facebetter', '1.2.2' end ``` Run the installation command: ```bash theme={null} pod install ``` **Xcode 15+ Compilation Error Handling** If you are using **Xcode 15** or later, you might encounter a `Sandbox: rsync.samba deny(1)` error during compilation. This is caused by Xcode's default **User Script Sandboxing** being enabled. **Solution:** 1. Select your **Project** in Xcode. 2. Navigate to the **Build Settings** tab. 3. Search for `ENABLE_USER_SCRIPT_SANDBOXING`. 4. Change its value from `Yes` to **`No`**. ### Method B: Manual Framework Integration Go to the [Download](https://facebetter.net/download) page to get the latest SDK, then extract it. Copy the `Facebetter.framework` library from the SDK package to your project path. Open Xcode and [refer to this guide](https://help.apple.com/xcode/mac/current/#/dev51a648b07) to add the `Facebetter.framework` dynamic library. Make sure the **Embed** property of the added dynamic library is set to **Embed & Sign**. Xcode Link Library ### Permission Configuration Ensure network permissions are enabled for appkey validation Xcode Signing **Permission Descriptions:** * **Network Permission**: Required. SDK needs network connection to verify `appId` and `appKey` to ensure the app runs normally. ## Import Header Files ```objc theme={null} #import ``` ## Log Configuration Logging is disabled by default and can be enabled as needed. Both console logging and file logging switches are supported. Logging should be enabled **before** creating the beauty engine, otherwise you may not see initialization logs. ```objc theme={null} FBLogConfig* logConfig = [[FBLogConfig alloc] init]; // Log level logConfig.level = FBLogLevel_Info; // Console logging logConfig.consoleEnabled = YES; // File logging logConfig.fileEnabled = YES; logConfig.fileName = @"log path: xx/xx/facebetter.log"; ``` ## Create Configuration Engine Follow the instructions on [this page](/intro/enable-service#get-appid-and-appkey) to get your `appid` and `appkey`. **Verification Priority:** * If `licenseJson` is provided, use license data verification (supports online response and offline license) * Otherwise, use `appId` and `appKey` for automatic online verification ```objc{2,3,4} theme={null} FBEngineConfig *engineConfig = [[FBEngineConfig alloc] init]; engineConfig.appId = @"your appId"; // Configure your appid (optional, not required if licenseJson is provided) engineConfig.appKey = @"your appkey"; // Configure your appkey (optional, not required if licenseJson is provided) // Optional: Use license data verification (takes priority if provided) // engineConfig.licenseJson = @"your license json string"; self.beautyEffectEngine = [FBBeautyEffectEngine createEngineWithConfig:engineConfig]; ``` ### Error Handling After creating the engine, it's recommended to check if it was successful: ```objc theme={null} if (self.beautyEffectEngine == nil) { NSLog(@"Failed to create beauty engine"); return; } ``` ## Using Filters and Stickers Filters and stickers need to be registered as resource files (`.fbd`) first, and then set via their `ID`. ### Filters ```objc theme={null} // 1. Register filter resource (usually executed once after initialization) NSString *filterId = @"chuxin"; NSString *fbdPath = [[NSBundle mainBundle] pathForResource:@"chuxin" ofType:@"fbd"]; [self.beautyEffectEngine registerFilter:filterId fbdFilePath:fbdPath]; // 2. Set filter [self.beautyEffectEngine setFilter:filterId]; // 3. Adjust intensity (0.0 - 1.0) [self.beautyEffectEngine setFilterIntensity:0.8f]; // 4. Clear filter [self.beautyEffectEngine setFilter:nil]; ``` ### Stickers ```objc theme={null} // 1. Register sticker resource NSString *stickerId = @"glasses"; NSString *fbdPath = [[NSBundle mainBundle] pathForResource:@"glasses" ofType:@"fbd"]; [self.beautyEffectEngine registerSticker:stickerId fbdFilePath:fbdPath]; // 2. Set sticker [self.beautyEffectEngine setSticker:stickerId]; // 3. Clear sticker [self.beautyEffectEngine setSticker:nil]; ``` ## Adjust Beauty Parameters All beauty parameters range from `[0.0, 1.0]`. Set to `0` to disable the effect. ### Set Skin Beauty Parameters Use the `setBasicParam` interface to set skin beauty parameters. **Parameter range \[0.0, 1.0]**. ```objc theme={null} [self.beautyEffectEngine setBasicParam:FBBasicParam_Smoothing floatValue:0.5f]; ``` Supported skin beauty parameters: ```objc theme={null} typedef NS_ENUM(NSInteger, FBBasicParam) { FBBasicParam_Smoothing = 0, // Smoothing FBBasicParam_Sharpening, // Sharpening FBBasicParam_Whitening, // Whitening FBBasicParam_Rosiness, // Rosiness }; ``` ### Set Skin-Only Beauty Use the `setSkinOnlyBeauty:` interface to set whether beauty effects are applied only to skin regions. When enabled, beauty effects (smoothing, whitening, etc.) will only be applied to detected skin areas, leaving non-skin areas unchanged. ```objc theme={null} // Enable skin-only beauty [self.beautyEffectEngine setSkinOnlyBeauty:YES]; // Disable skin-only beauty (apply to entire image) [self.beautyEffectEngine setSkinOnlyBeauty:NO]; ``` After enabling skin-only beauty, even with high beauty parameter values, non-skin areas (such as background, clothing, etc.) will not be affected. ### Set Face Reshape Parameters Use the `setReshapeParam` interface to set face reshape parameters. **Parameter range \[0.0, 1.0]**. ```objc theme={null} [self.beautyEffectEngine setReshapeParam:FBReshapeParam_FaceThin floatValue:0.5f]; ``` Supported face reshape parameters: ```objc theme={null} typedef NS_ENUM(NSInteger, FBReshapeParam) { FBReshapeParam_FaceThin = 0, // Face thinning FBReshapeParam_FaceVShape, // V-shaped face FBReshapeParam_FaceNarrow, // Narrow face FBReshapeParam_FaceShort, // Short face FBReshapeParam_Cheekbone, // Cheekbone FBReshapeParam_Jawbone, // Jawbone FBReshapeParam_Chin, // Chin FBReshapeParam_NoseSlim, // Nose slimming FBReshapeParam_EyeSize, // Eye enlargement FBReshapeParam_EyeDistance, // Eye distance }; ``` ### Set Makeup Parameters ```objc theme={null} [self.beautyEffectEngine setMakeupParam:FBMakeupParam_Lipstick floatValue:0.5f]; ``` Supported makeup parameters: ```objc theme={null} typedef NS_ENUM(NSInteger, FBMakeupParam) { FBMakeupParam_Lipstick = 0, // Lipstick FBMakeupParam_Blush, // Blush }; ``` ### Set Virtual Background Enable virtual background through the `setVirtualBackground` interface: ```objc theme={null} // Set background mode FBVirtualBackgroundOptions *options = [[FBVirtualBackgroundOptions alloc] initWithMode:FBBackgroundModeBlur]; [self.beautyEffectEngine setVirtualBackground:options]; // Set background image (need to set to Image mode first) FBVirtualBackgroundOptions *imageOptions = [[FBVirtualBackgroundOptions alloc] initWithMode:FBBackgroundModeImage]; imageOptions.backgroundImage = backgroundImageFrame; // FBImageFrame object [self.beautyEffectEngine setVirtualBackground:imageOptions]; ``` ## Set Engine Callbacks Monitor engine events (license validation and engine initialization status): ```objc theme={null} FBEngineCallbacks *callbacks = [[FBEngineCallbacks alloc] init]; callbacks.onEngineEvent = ^(FBEngineEventCode code, NSString* _Nullable message) { if (code == FBEngineEventCodeLicenseValidationSuccess) { // License validation succeeded NSLog(@"License validation succeeded"); } else if (code == FBEngineEventCodeLicenseValidationFailed) { // License validation failed NSLog(@"License validation failed: %@", message); } else if (code == FBEngineEventCodeInitializationComplete) { // Engine initialization completed NSLog(@"Engine initialization completed"); } else if (code == FBEngineEventCodeInitializationFailed) { // Engine initialization failed NSLog(@"Engine initialization failed: %@", message); } }; [self.beautyEffectEngine setCallbacks:callbacks]; ``` Event codes: * `FBEngineEventCodeLicenseValidationSuccess` (0): License validation succeeded * `FBEngineEventCodeLicenseValidationFailed` (1): License validation failed * `FBEngineEventCodeInitializationComplete` (100): Engine initialization completed * `FBEngineEventCodeInitializationFailed` (101): Engine initialization failed ## Process Images ### Create Images Image data is encapsulated through `FBImageFrame`, supporting formats: `YUVI420`, `NV12`, `NV21`, `RGB`, `RGBA`, `BGR`, `BGRA`. **Create FBImageFrame with RGBA** ```objc theme={null} FBImageFrame *input_image = [FBImageFrame createWithRGBA:data width:width height:height stride:stride]; ``` **Create FBImageFrame with image file** ```objc theme={null} FBImageFrame *input_image = [FBImageFrame createWithFile:@"xxx.png"]; ``` ### Rotate Images FBImageFrame has built-in image rotation methods that can be used as needed. ```objc theme={null} - (int)rotate:(FBImageRotation)rotation; ``` Rotation angles ```objc theme={null} typedef NS_ENUM(NSInteger, FBImageRotation) { FBImageRotation0, // 0 degrees FBImageRotation90, // Clockwise 90 degrees FBImageRotation180, // Clockwise 180 degrees FBImageRotation270, // Clockwise 270 degrees }; ``` ### Process Images `processMode` includes Video and Image modes. Video mode is suitable for live streaming and video scenarios with higher efficiency. Image mode is suitable for image processing scenarios. ```objc theme={null} input_image.type = FBFrameTypeVideo; FBImageFrame *output_image = [self.beautyEffectEngine processImage:input_image]; ``` The engine automatically maintains input/output format consistency. If input is RGBA format, output is RGBA format; if input is I420 format, output is I420 format. ### Get Processed Image Data ```objc theme={null} FBImageBuffer* buffer = [output_image toRGBA]; uint8_t* data = [buffer data]; int data_size = buffer.size; int width = buffer.width; int height = buffer.width; int stride = buffer.stride; ``` Get I420 data ```objc theme={null} FBImageBuffer* buffer = [output_image toI420]; // Get continuous I420 memory data uint8_t* data = [buffer data]; // Get I420 data length int data_size = buffer.size; // Get Y, U, V component data separately uint8_t* dataY = [buffer dataY]; uint8_t* dataU = [buffer dataU]; uint8_t* dataV = [buffer dataV]; int strideY = buffer.strideY; int strideU = buffer.strideU; int strideV = buffer.strideV; ``` `FBImageFrame` can be converted to various formats through built-in toXXX methods: `YUVI420`, `NV12`, `NV21`, `RGB`, `RGBA`, `BGR`, `BGRA`. These methods can be used for format conversion. ## Lifecycle Management FBBeautyEffectEngine is a singleton and is automatically released when the app ends. Manual management is not required. ### Release Resources When ViewController is destroyed, be sure to release engine resources: ```objc theme={null} - (void)dealloc { if (self.beautyEffectEngine) { // Note: FBBeautyEffectEngine is a singleton, usually doesn't need manual release // But if there are custom cleanup logic, it can be handled here self.beautyEffectEngine = nil; } } ``` ### Memory Management * Release `FBImageFrame` and `FBImageBuffer` objects timely * Avoid repeatedly creating large numbers of image objects in loops * Recommend reusing `FBImageFrame` objects ```objc theme={null} // Release resources after use if (inputImage) { inputImage = nil; // ARC will automatically release } if (outputImage) { outputImage = nil; // ARC will automatically release } if (buffer) { buffer = nil; // ARC will automatically release } ``` ## Related Documentation * [Best Practices](/macos/best-practices) - Performance optimization and architecture design recommendations * [Common Issues](/macos/faq) - Common questions and troubleshooting * [API Reference](/macos/api-reference) - Complete API documentation # Quick Start Source: https://facebetter.mintlify.app/macos/quick-start Run macOS Sample Application ## Environment Requirements * **macOS Version**: `macOS 10.14` and above * **Development Language**: `Objective-C` * **Architecture Support**: `x86_64`, `arm64` (Apple Silicon) ## Get Sample Source Code Clone the [GitHub repository](https://github.com/pixpark/facebetter-sdk) locally, and navigate to the `demo/macos` directory. ```bash theme={null} # clone git clone https://github.com/pixpark/facebetter-sdk.git # Enter macOS Demo directory cd facebetter-sdk/demo/macos # Install dependencies pod install ``` **Xcode 15+ Compilation Error Handling** If you are using **Xcode 15** or later, you might encounter a `Sandbox: rsync.samba deny(1)` error during compilation. This is caused by Xcode's default **User Script Sandboxing** being enabled. **Solution:** 1. Select your **Project** in Xcode. 2. Navigate to the **Build Settings** tab. 3. Search for `ENABLE_USER_SCRIPT_SANDBOXING`. 4. Change its value from `Yes` to **`No`**. ## Configure Application Information and Keys ### Bind Application Bundle ID Follow the instructions on [this page](/intro/enable-service#bind-application-information) to bind your macOS application Bundle ID in the console, for example: `com.example.app` ### Get AppID and AppKey Follow the instructions on [this page](/intro/enable-service#get-appid-and-appkey) to get your `appid` and `appkey`. Open `ViewController.m` in the project and modify the `appid` and `appkey`. ```objc theme={null} FBEngineConfig *engineConfig = [[FBEngineConfig alloc] init]; engineConfig.appId = @"your appId"; engineConfig.appKey = @"your appkey"; // Optional: If licenseJson is provided, license data verification takes priority, appId and appKey are not required // engineConfig.licenseJson = @"your license json string"; self.beautyEffectEngine = [FBBeautyEffectEngine createEngineWithConfig:engineConfig]; ``` `licenseJson` takes priority over `appId` + `appKey`. For offline download and details, see [License & Auth](/intro/license). ## Run the Project Open `demo/macos/FBExampleObjc.xcworkspace` 1. Ensure Xcode project signing is correct Xcode Signing 2. Ensure network permissions are enabled for appkey validation Xcode Signing 3. Build and run Select target device (Mac or Simulator) and click the build and run button. # API Reference Source: https://facebetter.mintlify.app/web/api-reference Web API Reference ## Error Classes ### FacebetterError Facebetter error class, extends `Error`. **Properties:** * `message`: Error message * `code`: Error code (default -1) * `name`: Error name (fixed as 'FacebetterError') ```javascript theme={null} class FacebetterError extends Error { constructor(message, code = -1) } ``` **Example:** ```javascript theme={null} try { // Some operation } catch (error) { if (error instanceof FacebetterError) { console.error('Facebetter error:', error.message); console.error('Error code:', error.code); } } ``` ## Configuration Classes ### EngineConfig Engine configuration class, used to initialize the beauty engine. **Constructor:** ```javascript theme={null} new EngineConfig(config) ``` **Parameters:** * `config.appId` (string, optional): Application ID (required if licenseJson is not provided) * `config.appKey` (string, optional): Application key (required if licenseJson is not provided) * `config.licenseJson` (string, optional): License JSON string (if provided, appId and appKey are not needed) * `config.externalContext` (boolean, optional): Whether to use external OpenGL context (reserved for configuration structure alignment in Web/WASM environment, but currently not effective) **Methods:** * `isValid()`: Validate if configuration is valid * `toString()`: Return string representation of configuration **Verification Priority:** * If `licenseJson` is not empty, use license data verification (supports online response and offline license) * Otherwise, use `appId` and `appKey` for automatic online verification **Example:** ```javascript theme={null} const config = new EngineConfig({ appId: 'your-app-id', appKey: 'your-app-key' }); // Or use licenseJson const config = new EngineConfig({ licenseJson: '{"license": "..."}' }); ``` ## Enumeration Types ### BeautyType Beauty type enumeration. ```javascript theme={null} BeautyType = { Basic: 0, // Basic beauty Reshape: 1, // Face reshaping Makeup: 2, // Makeup effects VirtualBackground: 3 // Virtual background }; ``` ### BasicParam Basic beauty parameter enumeration. ```javascript theme={null} BasicParam = { Smoothing: 0, // Skin smoothing Sharpening: 1, // Sharpening Whitening: 2, // Whitening Rosiness: 3 // Rosiness }; ``` ### ReshapeParam Face reshape parameter enumeration. ```javascript theme={null} ReshapeParam = { FaceThin: 0, // Face thinning FaceVShape: 1, // V-face FaceNarrow: 2, // Narrow face FaceShort: 3, // Short face Cheekbone: 4, // Cheekbone Jawbone: 5, // Jawbone Chin: 6, // Chin NoseSlim: 7, // Nose slimming EyeSize: 8, // Eye enlargement EyeDistance: 9 // Eye distance }; ``` ### MakeupParam Makeup parameter enumeration. ```javascript theme={null} MakeupParam = { Lipstick: 0, // Lipstick Blush: 1 // Blush }; ``` ### LipstickStyle Lipstick style enumeration. ```javascript theme={null} LipstickStyle = { Rouge: 0, // Rose red Coral: 1, // Coral Pink: 2 // Pink (default) }; ``` ### BlushStyle Blush style enumeration. ```javascript theme={null} BlushStyle = { Classic: 0, // Classic (default) Peach: 1, // Peach Rose: 2 // Rose }; ``` ### MirrorMode Mirror mode enumeration, applied to input before processing. ```javascript theme={null} MirrorMode = { None: 0, // No mirror Horizontal: 1, // Mirror horizontally (e.g. front camera selfie) Vertical: 2, // Mirror vertically Both: 3 // Mirror both axes }; ``` ### Resource Management * `setFilter(filterId)`: Set filter * Parameters: `filterId` (string) unique filter identifier. Pass an empty string to clear. * `setFilterIntensity(intensity)`: Set filter intensity * Parameters: `intensity` (number) intensity value, range \[0.0, 1.0]. * `setSticker(stickerId)`: Set sticker * Parameters: `stickerId` (string) unique sticker identifier. Pass an empty string to clear. * `registerFilter(filterId, resource)`: Register filter * Parameters: * `filterId` (string): Unique filter identifier * `resource` (string|Uint8Array): Resource path (.fbd file) or Uint8Array data * `registerSticker(stickerId, resource)`: Register sticker * Parameters: * `stickerId` (string): Unique sticker identifier * `resource` (string|Uint8Array): Resource path (.fbd file) or Uint8Array data * `unregisterFilter(filterId)`: Unload filter * `unregisterAllFilters()`: Unload all filters * `unregisterSticker(stickerId)`: Unload sticker * `unregisterAllStickers()`: Unload all stickers * `getRegisteredFilters()`: Get list of registered filters * Returns: `string[]` * `getRegisteredStickers()`: Get list of registered stickers * Returns: `string[]` ### ProcessMode Processing mode enumeration. ```javascript theme={null} ProcessMode = { Image: 0, // Image mode, suitable for single image processing Video: 1 // Video mode, suitable for video streams and live scenarios, better performance }; ``` ### BackgroundMode Background mode enumeration. ```javascript theme={null} BackgroundMode = { None: 0, // No background processing Blur: 1, // Blur background Image: 2 // Background image replacement }; ``` ### VirtualBackgroundOptions Virtual background options class, used to set virtual background parameters. **Constructor:** ```javascript theme={null} new VirtualBackgroundOptions(options) ``` **Parameters:** * `options` (Object, optional): Options object * `mode` (BackgroundMode, optional): Background mode, defaults to `BackgroundMode.None` * `backgroundImage` (ImageData|HTMLImageElement|HTMLCanvasElement, optional): Background image, required when mode is `Image` **Methods:** * `isValid()`: Validate if options are valid * Returns: `boolean` * When mode is `Image`, checks if `backgroundImage` exists **Example:** ```javascript theme={null} import { VirtualBackgroundOptions, BackgroundMode } from 'facebetter'; // Create blur background options const blurOptions = new VirtualBackgroundOptions({ mode: BackgroundMode.Blur }); // Create image background options const bgImage = new Image(); bgImage.src = 'background.jpg'; const imageOptions = new VirtualBackgroundOptions({ mode: BackgroundMode.Image, backgroundImage: bgImage }); // Validate options if (imageOptions.isValid()) { engine.setVirtualBackground(imageOptions); } ``` ## Engine Classes ### BeautyEffectEngine Main beauty effect engine class, provides entry point for beauty functionality. **Constructor:** ```javascript theme={null} new BeautyEffectEngine(config) ``` **Parameters:** * `config` (EngineConfig): Engine configuration object **Instance Methods:** #### Initialization * `init(options)`: Initialize engine * Parameters: * `options` (Object, optional): Initialization options * `timeout` (number, optional): WASM module loading timeout in milliseconds, default 30000 * `authTimeout` (number, optional): Online authentication timeout in milliseconds, default 10000 * Returns: `Promise` * Example: ```javascript theme={null} await engine.init(); // Or specify timeout await engine.init({ timeout: 60000, // WASM loading timeout 60 seconds authTimeout: 15000 // Authentication timeout 15 seconds }); ``` * `setLogConfig(config)`: Set log configuration * Parameters: * `config.consoleEnabled` (boolean, optional): Enable console logging, default false * `config.fileEnabled` (boolean, optional): Enable file logging, default false (not supported in browser environment) * `config.level` (number, optional): Log level (0=DEBUG, 1=INFO, 2=WARN, 3=ERROR), default 0 * `config.fileName` (string, optional): Log file name, default empty string * Returns: `Promise` * Note: Can be called before or after `init()`, but recommended to call before `init()` #### Parameter Settings * `setBasicParam(param, value)`: Set basic beauty parameter * Parameters: * `param` (BasicParam): Parameter type * `value` (number): Parameter value, range \[0.0, 1.0] (float) * Returns: `void` * Example: `engine.setBasicParam(BasicParam.Whitening, 0.5);` * `setReshapeParam(param, value)`: Set face reshape parameter * Parameters: * `param` (ReshapeParam): Parameter type * `value` (number): Parameter value, range \[0.0, 1.0] (float) * Returns: `void` * Example: `engine.setReshapeParam(ReshapeParam.FaceThin, 0.5);` * `setMakeupParam(param, value)`: Set makeup parameter * Parameters: * `param` (MakeupParam): Parameter type * `value` (number): Parameter value, range \[0.0, 1.0] (float) * Returns: `void` * Example: `engine.setMakeupParam(MakeupParam.Lipstick, 0.5);` * `setLipstickStyle(style)`: Set lipstick style * Parameters: * `style` (LipstickStyle): Lipstick style * Returns: `void` * Example: `engine.setLipstickStyle(LipstickStyle.Rouge);` * `setBlushStyle(style)`: Set blush style * Parameters: * `style` (BlushStyle): Blush style * Returns: `void` * Example: `engine.setBlushStyle(BlushStyle.Classic);` * `setSkinOnlyBeauty(enabled)`: Set whether beauty is applied only to skin regions * Parameters: * `enabled` (boolean): `true` to enable skin-only beauty, `false` to apply to entire image * Returns: `void` * Example: ```javascript theme={null} // Enable skin-only beauty engine.setSkinOnlyBeauty(true); // Disable skin-only beauty engine.setSkinOnlyBeauty(false); ``` * `setVirtualBackground(options)`: Set virtual background (unified API, consistent with other platforms) * Parameters: * `options` (VirtualBackgroundOptions|Object): Virtual background options * `mode` (BackgroundMode): Background mode (None, Blur, Image) * `backgroundImage` (ImageData|HTMLImageElement|HTMLCanvasElement, optional): Background image (required when mode is Image) * Returns: `void` * Example: ```javascript theme={null} import { VirtualBackgroundOptions, BackgroundMode } from 'facebetter'; // Set blur background const blurOptions = new VirtualBackgroundOptions({ mode: BackgroundMode.Blur }); engine.setVirtualBackground(blurOptions); // Set image background const bgImage = new Image(); bgImage.onload = () => { const imageOptions = new VirtualBackgroundOptions({ mode: BackgroundMode.Image, backgroundImage: bgImage }); engine.setVirtualBackground(imageOptions); }; bgImage.src = 'background.jpg'; // Simplified syntax engine.setVirtualBackground({ mode: BackgroundMode.Blur }); ``` #### Image Processing * `processImage(input, width, height, frameType, mirrorMode)`: Process image * Parameters: * `input` (ImageData | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | Uint8ClampedArray): Input image * `width` (number, optional): Image width (required when input is Uint8ClampedArray) * `height` (number, optional): Image height (required when input is Uint8ClampedArray) * `frameType` (FrameType, optional): Frame type, defaults to `FrameType.Video` * `mirrorMode` (MirrorMode, optional): Mirror mode applied to input before processing, defaults to `MirrorMode.None` * Returns: `ImageData` (synchronous return, not Promise) * Example: ```javascript theme={null} // Using ImageData const result = engine.processImage( imageData, 640, 480, FrameType.Video, MirrorMode.Horizontal ); ``` #### Resource Management * `destroy()`: Destroy engine and release resources * Returns: `void` * Note: Call when engine is no longer needed to free memory and WASM resources * Example: `engine.destroy();` ## Utility Functions ### loadWasmModule Load WASM module (usually not needed to call directly, engine handles automatically). ```javascript theme={null} import { loadWasmModule } from 'facebetter'; const module = await loadWasmModule(); ``` ## Usage Examples ### Complete Example ```javascript theme={null} import { BeautyEffectEngine, EngineConfig, BeautyType, BasicParam, FrameType } from 'facebetter'; // Create configuration const config = new EngineConfig({ appId: 'your-app-id', appKey: 'your-app-key' }); // Create engine const engine = new BeautyEffectEngine(config); // Set logging await engine.setLogConfig({ consoleEnabled: true, level: 1 }); // Initialize await engine.init(); // Set parameters engine.setBasicParam(BasicParam.Whitening, 0.5); engine.setBasicParam(BasicParam.Smoothing, 0.5); // Process image const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const result = engine.processImage( imageData, canvas.width, canvas.height, ProcessMode.Video ); // Draw result ctx.putImageData(result, 0, 0); // Release resources engine.destroy(); ``` ## Deprecated APIs **Deprecated** The following APIs are deprecated. ### Beauty Type Control * `setBeautyTypeEnabled(beautyType, enabled)` * **Description**: \[Deprecated] Enable or disable beauty type (No effect in parameter-driven mode) * **Return Value**: `void` * `isBeautyTypeEnabled(beautyType)` * **Description**: \[Deprecated] Check if beauty type is enabled (Always returns false) * **Return Value**: `false` * `disableAllBeautyTypes()` * **Description**: \[Deprecated] Disable all beauty types (Please reset effects by zeroing parameters) * **Return Value**: `void` # Best Practices Source: https://facebetter.mintlify.app/web/best-practices Web Beauty SDK Best Practices Guide ## Performance Optimization ### 1. Choose Appropriate Processing Mode **Video Mode (ProcessMode.Video)** * Suitable for real-time video streams and live scenarios * Better performance, faster processing * Recommended for camera preview, video calls, etc. **Image Mode (ProcessMode.Image)** * Suitable for single image processing * Higher quality, better effects * Recommended for photo editing, image beautification, etc. ```javascript theme={null} // Real-time video processing (synchronous method) const result = engine.processImage( imageData, imageData.width, imageData.height, ProcessMode.Video ); // High-quality image processing (synchronous method) const result = engine.processImage( imageData, imageData.width, imageData.height, ProcessMode.Image ); ``` ### 2. Parameter Adjustment Recommendations **Beauty Parameter Adjustment Principles** * Start with smaller values to avoid over-beautification * In real-time scenarios, recommend lowering parameter values for smoothness * Static images can use higher parameter values * Adjust parameter ranges based on user groups and scenarios **Recommended Parameter Ranges** ```javascript theme={null} // Basic beauty parameters (real-time scenarios) engine.setBasicParam(BasicParam.Smoothing, 0.2); // Skin smoothing (0.0-1.0) engine.setBasicParam(BasicParam.Whitening, 0.1); // Whitening (0.0-1.0) engine.setBasicParam(BasicParam.Rosiness, 0.1); // Rosiness (0.0-1.0) // Face reshape parameters (real-time scenarios) engine.setReshapeParam(ReshapeParam.FaceThin, 0.1); // Face thinning (0.0-1.0) engine.setReshapeParam(ReshapeParam.EyeSize, 0.1); // Eye enlargement (0.0-1.0) // Static images can use higher parameter values engine.setBasicParam(BasicParam.Smoothing, 0.5); engine.setBasicParam(BasicParam.Whitening, 0.3); ``` ### 3. Memory Optimization **Reuse Canvas and ImageData Objects** ```javascript theme={null} class ImageProcessor { constructor() { this.canvas = null; this.ctx = null; this.imageData = null; } initCanvas(width, height) { if (!this.canvas) { this.canvas = document.createElement('canvas'); this.canvas.width = width; this.canvas.height = height; this.ctx = this.canvas.getContext('2d'); } else { // Update Canvas if dimensions change if (this.canvas.width !== width || this.canvas.height !== height) { this.canvas.width = width; this.canvas.height = height; } } } processFrame(videoElement, engine) { this.initCanvas(videoElement.videoWidth, videoElement.videoHeight); // Draw video frame to Canvas this.ctx.drawImage(videoElement, 0, 0); // Get image data (reuse ImageData) this.imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height); // Process image (synchronous method) const result = engine.processImage( this.imageData, this.canvas.width, this.canvas.height, ProcessMode.Video ); // Draw result this.ctx.putImageData(result, 0, 0); return this.canvas; } } ``` **Release Resources Promptly** ```javascript theme={null} // Release resources on page unload window.addEventListener('beforeunload', () => { if (engine) { engine.destroy(); engine = null; } }); // Vue component onUnmounted(() => { if (engine) { engine.destroy(); engine = null; } }); // React component useEffect(() => { return () => { if (engine) { engine.destroy(); engine = null; } }; }, []); ``` ### 4. Use Web Worker (Optional) For large amounts of image processing, consider using Web Worker to avoid blocking the main thread: ```javascript theme={null} // worker.js import { BeautyEffectEngine, EngineConfig } from 'facebetter'; let engine = null; self.onmessage = async function(e) { const { type, data } = e.data; switch (type) { case 'init': try { const config = new EngineConfig(data.config); engine = new BeautyEffectEngine(config); await engine.init(); self.postMessage({ type: 'init', success: true }); } catch (error) { self.postMessage({ type: 'init', success: false, error: error.message }); } break; case 'process': try { // processImage is a synchronous method const result = engine.processImage( data.imageData, data.options.width, data.options.height, data.options.processMode || ProcessMode.Video ); self.postMessage({ type: 'process', success: true, result: result }); } catch (error) { self.postMessage({ type: 'process', success: false, error: error.message }); } break; } }; ``` ## Architecture Design ### 1. Singleton Pattern for Engine Management ```javascript theme={null} class BeautyEngineManager { static instance = null; static engine = null; static async getInstance(config) { if (!this.instance) { this.instance = new BeautyEngineManager(); await this.instance.init(config); } return this.instance; } async init(config) { if (!this.engine) { const engineConfig = new EngineConfig(config); this.engine = new BeautyEffectEngine(engineConfig); await this.engine.init(); } } getEngine() { return this.engine; } destroy() { if (this.engine) { this.engine.destroy(); this.engine = null; } this.instance = null; } } // Usage const manager = await BeautyEngineManager.getInstance({ appId: 'your-app-id', appKey: 'your-app-key' }); const engine = manager.getEngine(); ``` ### 2. Parameter Management Class ```javascript theme={null} class BeautyParamManager { constructor(engine) { this.engine = engine; this.params = { basic: { smoothing: 0, whitening: 0, rosiness: 0 }, reshape: { faceThin: 0, eyeSize: 0 }, makeup: { lipstick: 0, blush: 0 } }; } setBasicParam(type, value) { this.params.basic[type] = value; this.engine.setBasicParam(BasicParam[type], value); } setReshapeParam(type, value) { this.params.reshape[type] = value; this.engine.setReshapeParam(ReshapeParam[type], value); } setMakeupParam(type, value) { this.params.makeup[type] = value; this.engine.setMakeupParam(MakeupParam[type], value); } reset() { Object.keys(this.params.basic).forEach(key => { this.setBasicParam(key, 0); }); Object.keys(this.params.reshape).forEach(key => { this.setReshapeParam(key, 0); }); Object.keys(this.params.makeup).forEach(key => { this.setMakeupParam(key, 0); }); } getParams() { return JSON.parse(JSON.stringify(this.params)); } } ``` ### 3. Image Processing Pipeline ```javascript theme={null} class ImageProcessingPipeline { constructor(engine) { this.engine = engine; this.canvas = document.createElement('canvas'); this.ctx = this.canvas.getContext('2d'); } async processImage(source, options = {}) { try { // 1. Prepare Canvas this.canvas.width = source.width || source.videoWidth; this.canvas.height = source.height || source.videoHeight; // 2. Draw source image this.ctx.drawImage(source, 0, 0); // 3. Get image data const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height); // 4. Process image (synchronous method, no await needed) const result = this.engine.processImage( imageData, this.canvas.width, this.canvas.height, options.processMode || ProcessMode.Video ); // 5. Draw result this.ctx.putImageData(result, 0, 0); return this.canvas; } catch (error) { console.error('Image processing failed:', error); throw error; } } } ``` ## User Experience Optimization ### 1. Loading Status Indication ```javascript theme={null} async function initEngineWithLoading(config) { showLoading('Initializing beauty engine...'); try { const engine = new BeautyEffectEngine(config); await engine.init(); hideLoading(); showSuccess('Beauty engine initialized successfully'); return engine; } catch (error) { hideLoading(); showError('Beauty engine initialization failed: ' + error.message); throw error; } } ``` ### 2. Progressive Loading ```javascript theme={null} async function initEngineProgressive(config) { // 1. Show loading prompt updateLoadingStatus('Loading WASM module...'); const engine = new BeautyEffectEngine(config); // 2. Initialize (will load WASM internally) updateLoadingStatus('Initializing engine...'); await engine.init(); // 3. Configure beauty parameters updateLoadingStatus('Configuring beauty parameters...'); engine.setBasicParam(BasicParam.Smoothing, 0.5); updateLoadingStatus('Complete'); return engine; } ``` ### 3. Error Retry Mechanism ```javascript theme={null} async function initEngineWithRetry(config, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { const engine = new BeautyEffectEngine(config); await engine.init(); return engine; } catch (error) { if (i < maxRetries - 1) { console.warn(`Initialization failed, retrying in ${1000 * (i + 1)}ms...`); await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); } else { throw error; } } } } ``` ## Security Recommendations ### 1. Protect AppID and AppKey **Don't hardcode keys in frontend code:** ```javascript theme={null} // ❌ Not recommended: hardcoded in code const config = new EngineConfig({ appId: 'your-app-id', appKey: 'your-app-key' // Or use licenseJson: 'your license json string' }); // ✅ Recommended: fetch from server or use environment variables async function getConfig() { const response = await fetch('/api/config'); const { appId, appKey, licenseJson } = await response.json(); // If licenseJson is provided, use it; otherwise use appId and appKey return new EngineConfig(licenseJson ? { licenseJson } : { appId, appKey }); } ``` ### 2. Use HTTPS Ensure using HTTPS in production environment to protect data transmission security. ### 3. Validate User Input ```javascript theme={null} function validateConfig(config) { if (!config.appId || typeof config.appId !== 'string') { throw new Error('appId must be a string'); } if (!config.appKey || typeof config.appKey !== 'string') { throw new Error('appKey must be a string'); } return true; } ``` ## Related Documentation * [Error Handling](/web/error-handling) - Detailed error handling guide * [FAQ](/web/faq) - Common questions and solutions * [API Reference](/web/api-reference) - Complete API documentation # Error Handling Source: https://facebetter.mintlify.app/web/error-handling Web Beauty SDK Error Handling Guide ## Error Handling Basics ### 1. Use try-catch All asynchronous operations should use try-catch for error handling: ```javascript theme={null} try { const config = new EngineConfig({ appId: 'your-app-id', appKey: 'your-app-key' // Optional: If licenseJson is provided, license data verification takes priority // licenseJson: 'your license json string' }); const engine = new BeautyEffectEngine(config); await engine.init(); console.log('Engine initialized successfully'); } catch (error) { if (error instanceof FacebetterError) { console.error('Facebetter error:', error.message); console.error('Error code:', error.code); } else { console.error('Unknown error:', error); } } ``` ### 2. Check Engine Initialization ```javascript theme={null} let engine = null; try { const config = new EngineConfig({ appId: 'your-app-id', appKey: 'your-app-key' }); engine = new BeautyEffectEngine(config); await engine.init(); if (!engine || !engine.initialized) { throw new Error('Engine initialization failed'); } } catch (error) { console.error('Engine initialization failed:', error); // Handle error, e.g., show user prompt showErrorToUser('Beauty engine initialization failed, please refresh the page and try again'); } ``` ### 3. Check API Call Results ```javascript theme={null} try { engine.setBasicParam(BasicParam.Smoothing, 0.5); console.log('Successfully set basic beauty parameters'); } catch (error) { console.error('Failed to set basic beauty parameters:', error); } ``` ## Common Error Codes ### FacebetterError Error Codes FacebetterError error codes can be numbers or strings: **Numeric error codes** (from WASM layer): * **0**: Success * **-1**: General error (engine not initialized, invalid parameters, etc.) * **Other negative numbers**: Error codes returned by specific operations **String error codes** (from JS layer): * **'TIMEOUT'**: Operation timeout (WASM loading timeout, authentication timeout, etc.) * **'NETWORK\_ERROR'**: Network error (online authentication failure, etc.) * **'WASM\_LOAD\_ERROR'**: WASM module loading failed * **'LICENSE\_ERROR'**: License verification failed * **'AUTH\_TIMEOUT'**: Online authentication timeout * **'AUTH\_EMPTY\_RESPONSE'**: Online authentication returned empty response * **'ENGINE\_CREATE\_FAILED'**: Engine creation failed (may be due to license or resource path issues) * **'UNKNOWN\_ERROR'**: Unknown error ```javascript theme={null} try { await engine.init(); } catch (error) { if (error instanceof FacebetterError) { if (typeof error.code === 'string') { // String error code switch (error.code) { case 'TIMEOUT': console.error('Operation timeout:', error.message); break; case 'NETWORK_ERROR': console.error('Network error:', error.message); break; case 'WASM_LOAD_ERROR': console.error('WASM loading failed:', error.message); break; case 'LICENSE_ERROR': case 'ENGINE_CREATE_FAILED': console.error('License error:', error.message); break; default: console.error('Error:', error.code, error.message); } } else { // Numeric error code switch (error.code) { case -1: console.error('General error:', error.message); break; default: console.error('Error code:', error.code, error.message); } } } } ``` ## Image Data Error Handling ### 1. Input Image Validation ```javascript theme={null} function validateImageData(imageData) { if (!imageData) { throw new Error('Image data cannot be empty'); } if (!(imageData instanceof ImageData)) { throw new Error('Image data must be ImageData type'); } if (imageData.width <= 0 || imageData.height <= 0) { throw new Error('Invalid image dimensions'); } if (!imageData.data || imageData.data.length === 0) { throw new Error('Image data is empty'); } return true; } function processImageSafe(imageData, engine) { try { // Validate input validateImageData(imageData); // Process image (synchronous method) const result = engine.processImage( imageData, imageData.width, imageData.height, FrameType.Video ); return result; } catch (error) { console.error('Image processing failed:', error); throw error; } } ``` ### 2. Canvas Error Handling ```javascript theme={null} function getImageDataFromCanvas(canvas) { try { if (!canvas) { throw new Error('Canvas element does not exist'); } const ctx = canvas.getContext('2d'); if (!ctx) { throw new Error('Cannot get Canvas context'); } if (canvas.width === 0 || canvas.height === 0) { throw new Error('Invalid Canvas dimensions'); } return ctx.getImageData(0, 0, canvas.width, canvas.height); } catch (error) { console.error('Failed to get Canvas image data:', error); throw error; } } ``` ## Network Error Handling ### 1. Online Verification Error ```javascript theme={null} async function initEngineWithRetry(config, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { const engine = new BeautyEffectEngine(config); await engine.init(); return engine; } catch (error) { if (error.message.includes('network') || error.message.includes('fetch')) { console.warn(`Initialization failed (attempt ${i + 1}/${maxRetries}):`, error.message); if (i < maxRetries - 1) { // Wait before retry await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); continue; } } throw error; } } } // Usage try { const engine = await initEngineWithRetry(config); } catch (error) { console.error('Initialization failed after multiple retries:', error); } ``` ### 2. WASM Loading Error ```javascript theme={null} async function loadEngineWithFallback() { try { const engine = new BeautyEffectEngine(config); await engine.init(); return engine; } catch (error) { if (error.message.includes('WASM') || error.message.includes('wasm')) { console.error('WASM loading failed:', error); // Can try to reload or use fallback solution showErrorToUser('Beauty feature temporarily unavailable, please refresh the page and try again'); } throw error; } } ``` ## Error Handling Best Practices ### 1. Unified Error Handler ```javascript theme={null} class ErrorHandler { static handle(error, context = '') { console.error(`[${context}] Error:`, error); if (error instanceof FacebetterError) { // Facebetter specific error this.handleFacebetterError(error, context); } else if (error instanceof Error) { // General error this.handleGenericError(error, context); } else { // Unknown error console.error('Unknown error type:', error); } } static handleFacebetterError(error, context) { switch (error.code) { case -1: console.error('Facebetter general error:', error.message); break; default: console.error('Facebetter error:', error.code, error.message); } } static handleGenericError(error, context) { if (error.message.includes('network')) { console.error('Network error:', error.message); } else if (error.message.includes('WASM')) { console.error('WASM error:', error.message); } else { console.error('General error:', error.message); } } } // Usage try { await engine.init(); } catch (error) { ErrorHandler.handle(error, 'Engine initialization'); } ``` ### 2. User-Friendly Error Messages ```javascript theme={null} function showErrorToUser(message) { // Show user-friendly error message const errorDiv = document.createElement('div'); errorDiv.className = 'error-message'; errorDiv.textContent = message; document.body.appendChild(errorDiv); // Auto-hide after 3 seconds setTimeout(() => { errorDiv.remove(); }, 3000); } try { await engine.init(); } catch (error) { let userMessage = 'Beauty engine initialization failed'; if (error.message.includes('network')) { userMessage = 'Network connection failed, please check network and try again'; } else if (error.message.includes('WASM')) { userMessage = 'Browser not supported or loading failed, please refresh the page and try again'; } else if (error.message.includes('appId') || error.message.includes('appKey')) { userMessage = 'Configuration error, please contact technical support'; } showErrorToUser(userMessage); console.error('Detailed error:', error); } ``` ### 3. Error Logging ```javascript theme={null} class ErrorLogger { static log(error, context = '', userInfo = {}) { const errorLog = { timestamp: new Date().toISOString(), context: context, error: { message: error.message, code: error.code || 'N/A', stack: error.stack }, userInfo: userInfo, userAgent: navigator.userAgent, url: window.location.href }; // Send to server (optional) if (window.fetch) { fetch('/api/errors', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(errorLog) }).catch(err => console.error('Failed to send error log:', err)); } // Console output console.error('Error log:', errorLog); } } // Usage try { await engine.init(); } catch (error) { ErrorLogger.log(error, 'Engine initialization', { appId: config.appId ? 'provided' : 'missing' }); } ``` ## Preventive Error Handling ### 1. Parameter Validation ```javascript theme={null} function validateBeautyParam(value) { if (typeof value !== 'number') { throw new TypeError('Parameter value must be a number'); } if (value < 0.0 || value > 1.0) { throw new RangeError('Parameter value must be between 0.0-1.0'); } return true; } function setBasicParamSafe(engine, param, value) { try { validateBeautyParam(value); engine.setBasicParam(param, value); } catch (error) { console.error('Failed to set parameter:', error); throw error; } } ``` ### 2. State Checking ```javascript theme={null} function processImageWithValidation(engine, imageData) { if (!engine) { throw new Error('Engine not created'); } if (!engine.initialized) { throw new Error('Engine not initialized'); } if (!imageData) { throw new Error('Image data cannot be empty'); } // Process image (synchronous method) return engine.processImage( imageData, imageData.width, imageData.height, FrameType.Video ); } ``` ## Related Documentation * [Best Practices](/web/best-practices) - Performance optimization and error prevention * [FAQ](/web/faq) - Common errors and solutions * [API Reference](/web/api-reference) - Complete API documentation # FAQ Source: https://facebetter.mintlify.app/web/faq Web Beauty SDK FAQ ## Integration Issues ### Q: What to do if engine creation fails? A: Check the following: * Confirm `appId` and `appKey` are correct * Check if network connection is normal (online verification required) * Check browser console for error messages * Confirm SDK version is latest * Check if browser supports modern web technologies ```javascript theme={null} // Check browser support if (typeof WebAssembly === 'undefined') { console.error('Browser does not support required web technologies'); } ``` ### Q: How to check if SDK loaded successfully? A: You can check in the following ways: ```javascript theme={null} // ES Module import { BeautyEffectEngine } from 'facebetter'; console.log('SDK imported successfully'); // CommonJS const { BeautyEffectEngine } = require('facebetter'); console.log('SDK imported successfully'); ``` ### Q: Module loading error at runtime? A: Possible causes and solutions: * **Network issue**: Check network connection, ensure npm registry is accessible * **CORS issue**: Ensure server has correct CORS headers configured * **File path error**: Check if file path is correct * **Browser not supported**: Use modern browsers (Chrome 57+, Firefox 52+, Safari 11+, or Edge 16+) ```javascript theme={null} // Check browser support if (typeof WebAssembly === 'undefined') { alert('Your browser version is too old, please use Chrome 57+, Firefox 52+, Safari 11+, or Edge 16+'); } ``` ### Q: How to update version? A: Update the npm package: ```bash theme={null} npm update facebetter ``` Or specify a version: ```bash theme={null} npm install facebetter@latest ``` ## Function Issues ### Q: Beauty effect is not obvious? A: You can try: * Increase beauty parameter values (range 0.0-1.0) * Ensure corresponding beauty type is enabled * Check if image quality is clear enough * Confirm face detection is working normally * Try using `FrameType.Image` mode for better effects ```javascript theme={null} // Increase parameter value engine.setBasicParam(BasicParam.Whitening, 0.8); // Increase from 0.5 to 0.8 // Use image mode const result = engine.processImage( imageData, imageData.width, imageData.height, FrameType.Image // Use image mode ); ``` ### Q: Beauty effect is excessive or distorted? A: Recommendations: * Lower beauty parameter values * Check if parameter combinations are reasonable * Avoid enabling too many beauty types simultaneously * Adjust parameters based on image quality ```javascript theme={null} // Lower parameter value engine.setBasicParam(BasicParam.Whitening, 0.2); // Lower from 0.5 to 0.2 // Set necessary beauty parameters engine.setBasicParam(BasicParam.Smoothing, 0.5); ``` ### Q: Virtual background not working? A: Check: * Set virtual background options * Check if background image is correctly loaded * Confirm image format is supported (PNG, JPG) * Check if image file exists and is readable ```javascript theme={null} import { VirtualBackgroundOptions, BackgroundMode } from 'facebetter'; // Set virtual background (automatically enables the effect) const options = new VirtualBackgroundOptions({ mode: BackgroundMode.Blur }); engine.setVirtualBackground(options); // Set blur background const blurOptions = new VirtualBackgroundOptions({ mode: BackgroundMode.Blur }); engine.setVirtualBackground(blurOptions); // Set image background const bgImage = new Image(); bgImage.onload = () => { const imageOptions = new VirtualBackgroundOptions({ mode: BackgroundMode.Image, backgroundImage: bgImage }); engine.setVirtualBackground(imageOptions); }; bgImage.src = '/path/to/background.png'; ``` ### Q: Processed image is black? A: Possible causes: * Image data format is incorrect * Canvas context acquisition failed * Image dimensions are 0 ```javascript theme={null} // Check image data if (!imageData || !imageData.data) { console.error('Invalid image data'); return; } // Check Canvas const canvas = document.getElementById('canvas'); if (!canvas) { console.error('Canvas element does not exist'); return; } const ctx = canvas.getContext('2d'); if (!ctx) { console.error('Cannot get Canvas context'); return; } // Check dimensions if (canvas.width === 0 || canvas.height === 0) { console.error('Invalid Canvas dimensions'); return; } ``` ## Performance Issues ### Q: Processing speed is slow? A: Optimization recommendations: * Use `FrameType.Video` for real-time processing * Lower image resolution * Reduce number of simultaneously enabled beauty types * Avoid image processing on main thread * Use OffscreenCanvas (if supported) ```javascript theme={null} // Use video mode (synchronous method) const result = engine.processImage( imageData, imageData.width, imageData.height, FrameType.Video // Video mode is faster ); // Lower resolution const smallCanvas = document.createElement('canvas'); smallCanvas.width = Math.floor(canvas.width / 2); smallCanvas.height = Math.floor(canvas.height / 2); const smallCtx = smallCanvas.getContext('2d'); smallCtx.drawImage(sourceCanvas, 0, 0, smallCanvas.width, smallCanvas.height); const smallImageData = smallCtx.getImageData(0, 0, smallCanvas.width, smallCanvas.height); ``` ### Q: High memory usage? A: Solutions: * Release `ImageData` objects promptly * Avoid frequently creating and destroying engine instances * Reuse Canvas and ImageData objects * Use object pool pattern to manage image buffers ```javascript theme={null} // Reuse Canvas let reusableCanvas = null; let reusableCtx = null; function getReusableCanvas(width, height) { if (!reusableCanvas) { reusableCanvas = document.createElement('canvas'); reusableCanvas.width = width; reusableCanvas.height = height; reusableCtx = reusableCanvas.getContext('2d'); } else if (reusableCanvas.width !== width || reusableCanvas.height !== height) { reusableCanvas.width = width; reusableCanvas.height = height; } return { canvas: reusableCanvas, ctx: reusableCtx }; } ``` ### Q: Application lagging or crashing? A: Troubleshooting steps: * Check if image processing is on main thread * Confirm resource release is complete * Check console for error messages * Check if parameter values are within valid range (0.0-1.0) * Use browser performance analysis tools ```javascript theme={null} // Optimize with requestAnimationFrame function processFrame() { requestAnimationFrame(() => { try { const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); // processImage is a synchronous method const result = engine.processImage( imageData, canvas.width, canvas.height, FrameType.Video ); ctx.putImageData(result, 0, 0); } catch (error) { console.error('Processing failed:', error); } // Continue next frame if (isProcessing) { processFrame(); } }); } ``` ## Compatibility Issues ### Q: Which browsers are supported? A: Modern browsers with WebAssembly support: * Chrome 57+ * Firefox 52+ * Safari 11+ * Edge 16+ ```javascript theme={null} // Detect browser support function checkBrowserSupport() { if (typeof WebAssembly === 'undefined') { return { supported: false, message: 'Your browser does not support WebAssembly' }; } return { supported: true, message: 'Browser supports WebAssembly' }; } ``` ### Q: What to note when using on mobile? A: Recommendations: * Lower image resolution to improve performance * Use `FrameType.Video` mode * Appropriately lower beauty parameter values * Pay attention to memory usage, release resources promptly ```javascript theme={null} // Mobile optimization const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); if (isMobile) { // Lower resolution canvas.width = Math.min(canvas.width, 640); canvas.height = Math.min(canvas.height, 480); // Use video mode processMode = FrameType.Video; // Lower parameter values engine.setBasicParam(BasicParam.Whitening, 0.3); } ``` ## Other Issues ### Q: How to use in Vue/React? A: Refer to the following examples: **Vue 3:** ```vue theme={null} ``` **React:** ```jsx theme={null} import { useEffect, useRef } from 'react'; import { BeautyEffectEngine, EngineConfig } from 'facebetter'; function BeautyComponent() { const canvasRef = useRef(null); const engineRef = useRef(null); useEffect(() => { async function init() { const config = new EngineConfig({ appId: 'your-app-id', appKey: 'your-app-key' }); engineRef.current = new BeautyEffectEngine(config); await engineRef.current.init(); } init(); return () => { if (engineRef.current) { engineRef.current.destroy(); } }; }, []); return ; } ``` ### Q: How to handle beauty effects after image upload? A: Example code: ```javascript theme={null} function handleFileUpload(event) { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = async function(e) { const img = new Image(); img.onload = async function() { const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const result = engine.processImage( imageData, canvas.width, canvas.height, FrameType.Image ); ctx.putImageData(result, 0, 0); // Display processed image const resultImg = document.createElement('img'); resultImg.src = canvas.toDataURL(); document.body.appendChild(resultImg); }; img.src = e.target.result; }; reader.readAsDataURL(file); } ``` ## Related Documentation * [Quick Start](/web/quick-start) - Quick integration guide * [Implement Beauty](/web/implement-beauty) - Detailed usage instructions * [Error Handling](/web/error-handling) - Error handling guide * [Best Practices](/web/best-practices) - Performance optimization recommendations * [API Reference](/web/api-reference) - Complete API documentation # Implement Beauty Source: https://facebetter.mintlify.app/web/implement-beauty Implement Web Beauty ## Integrate SDK Install via npm: ```bash theme={null} npm install facebetter ``` **Using ES Module (Recommended):** ```javascript theme={null} import { BeautyEffectEngine, EngineConfig, BeautyType, BasicParam, ReshapeParam, MakeupParam, BackgroundMode, VirtualBackgroundOptions, ProcessMode } from 'facebetter'; ``` **Using CommonJS:** ```javascript theme={null} const { BeautyEffectEngine, EngineConfig } = require('facebetter'); ``` ## Log Configuration Logging is disabled by default, you can enable it as needed: Log configuration must be called **before** engine initialization. ```javascript theme={null} await engine.setLogConfig({ consoleEnabled: true, // Console logging fileEnabled: false, // File logging (not supported in browser environment) level: 0, // Log level: 0=DEBUG, 1=INFO, 2=WARN, 3=ERROR fileName: 'facebetter.log' // Log file name (optional, only effective when fileEnabled is true) }); ``` **Note**: Log configuration must be called before engine initialization. ## Create Engine Configuration Follow the instructions on [this page](/intro/enable-service#get-appid-and-appkey) to get `appId` and `appKey` **Verification Priority:** * If `licenseJson` is provided, use license data verification (supports online response and offline license) * Otherwise, use `appId` and `appKey` for automatic online verification ```javascript theme={null} import { BeautyEffectEngine, EngineConfig } from 'facebetter'; const config = new EngineConfig({ appId: 'your appId', // Configure your appid (optional, not required if licenseJson is provided) appKey: 'your appKey' // Configure your appkey (optional, not required if licenseJson is provided) // Optional: Use license data verification (takes priority if provided) // licenseJson: 'your license json string' }); const engine = new BeautyEffectEngine(config); ``` ### Error Handling It's recommended to check if engine creation is successful: ```javascript theme={null} try { await engine.init(); console.log('Engine initialized successfully'); } catch (error) { console.error('Engine initialization failed:', error); // Handle error } ``` ## Adjust Beauty Parameters All beauty parameters range from `[0.0, 1.0]`. Set to `0` to disable the effect. ### Set Skin Beauty Parameters Use `setBasicParam` interface to set skin beauty parameters, **parameter range \[0.0, 1.0]** (float); ```javascript theme={null} import { BasicParam } from 'facebetter'; engine.setBasicParam( BasicParam.Smoothing, 0.5 // 0.0-1.0 ); ``` Supported skin beauty parameters: ```javascript theme={null} BasicParam = { Smoothing: 0, // Skin smoothing Sharpening: 1, // Sharpening Whitening: 2, // Whitening Rosiness: 3 // Rosiness }; ``` ### Set Face Reshape Parameters Use `setReshapeParam` interface to set face reshape parameters, **parameter range \[0.0, 1.0]** (float); ```javascript theme={null} import { ReshapeParam } from 'facebetter'; engine.setReshapeParam( ReshapeParam.FaceThin, 0.5 // 0.0-1.0 ); ``` Supported face reshape parameters: ```javascript theme={null} ReshapeParam = { FaceThin: 0, // Face thinning FaceVShape: 1, // V-face FaceNarrow: 2, // Narrow face FaceShort: 3, // Short face Cheekbone: 4, // Cheekbone Jawbone: 5, // Jawbone Chin: 6, // Chin NoseSlim: 7, // Nose slimming EyeSize: 8, // Eye enlargement EyeDistance: 9 // Eye distance }; ``` ### Set Makeup Parameters Use `setMakeupParam` interface to set makeup parameters, **parameter range \[0.0, 1.0]** (float); ```javascript theme={null} import { MakeupParam } from 'facebetter'; engine.setMakeupParam( MakeupParam.Lipstick, 0.5 // 0.0-1.0 ); ``` Supported makeup parameters: ```javascript theme={null} MakeupParam = { Lipstick: 0, // Lipstick Blush: 1 // Blush }; ``` ### Set Skin-Only Beauty Use the `setSkinOnlyBeauty` interface to set whether beauty effects are applied only to skin regions. When enabled, beauty effects (smoothing, whitening, etc.) will only be applied to detected skin areas, leaving non-skin areas unchanged. ```javascript theme={null} // Enable skin-only beauty engine.setSkinOnlyBeauty(true); // Disable skin-only beauty (apply to entire image) engine.setSkinOnlyBeauty(false); ``` After enabling skin-only beauty, even with high beauty parameter values, non-skin areas (such as background, clothing, etc.) will not be affected. ### Set Virtual Background Enable virtual background via `setVirtualBackground` interface (unified API, consistent with other platforms): ```javascript theme={null} import { VirtualBackgroundOptions, BackgroundMode } from 'facebetter'; // Set blur background const blurOptions = new VirtualBackgroundOptions({ mode: BackgroundMode.Blur }); engine.setVirtualBackground(blurOptions); // Set image background const bgImage = new Image(); bgImage.onload = () => { const imageOptions = new VirtualBackgroundOptions({ mode: BackgroundMode.Image, backgroundImage: bgImage }); engine.setVirtualBackground(imageOptions); }; bgImage.src = 'background.jpg'; // Disable virtual background const noneOptions = new VirtualBackgroundOptions({ mode: BackgroundMode.None }); engine.setVirtualBackground(noneOptions); ``` **Simplified syntax (also supported):** ```javascript theme={null} // Pass object directly, no need to use new VirtualBackgroundOptions engine.setVirtualBackground({ mode: BackgroundMode.Blur }); // Set image background bgImage.onload = () => { engine.setVirtualBackground({ mode: BackgroundMode.Image, backgroundImage: bgImage }); }; // Turn off virtual background engine.setVirtualBackground({ mode: BackgroundMode.None }); ``` ## Using Filters and Stickers ### Filter Functionality Filters are set through the `setFilter` interface. Filter resource files (`.fbd`) must be registered via `registerFilter` first. ```javascript theme={null} // 1. Register filter resource (supports URL path or Uint8Array data) const filterId = 'chuxin'; const fbdUrl = '/assets/filters/chuxin.fbd'; engine.registerFilter(filterId, fbdUrl); // 2. Use filter engine.setFilter(filterId); // 3. Adjust filter intensity (0.0 - 1.0) engine.setFilterIntensity(0.8); ``` ### Sticker Functionality Stickers are set through the `setSticker` interface and also need to be registered first. ```javascript theme={null} // 1. Register sticker resource const stickerId = 'cherry'; const fbdUrl = '/assets/stickers/cherry.fbd'; engine.registerSticker(stickerId, fbdUrl); // 2. Use sticker engine.setSticker(stickerId); // 3. Clear sticker engine.setSticker(''); ``` ## Set Engine Callbacks Monitor engine authentication and initialization status: ```javascript theme={null} engine.setEngineEventCallback((code, message) => { if (code === 0) { // License verification succeeded or engine initialization completed console.log('Engine event succeeded:', message); } else { // License verification failed or engine initialization failed console.error('Engine event failed (code ' + code + '):', message); } }); ``` Event codes: * `0`: License validation success or engine initialization complete * `1`: License validation failed * `101`: Engine initialization failed ## Process Images ### Get Image Data from Canvas ```javascript theme={null} const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); ``` ### Get Image Data from Image Element ```javascript theme={null} const img = new Image(); img.onload = function() { const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); // Process image... }; img.src = 'image.jpg'; ``` ### Process Image `processMode` includes Video and Image modes. Video is suitable for live streaming and video scenarios with higher efficiency, Image mode is suitable for image processing scenarios. The engine automatically maintains input/output format consistency. If input is RGBA format, output is RGBA format; if input is I420 format, output is I420 format. ```javascript theme={null} import { FrameType } from 'facebetter'; // processImage is a synchronous method (no await needed) // Method 1: Pass ImageData with dimensions const result = engine.processImage( imageData, canvas.width, canvas.height, FrameType.Video // or FrameType.Image ); // Method 2: Pass HTMLImageElement (width/height can be omitted) const result = engine.processImage( imageElement, undefined, undefined, FrameType.Image ); // Method 3: Pass HTMLVideoElement (width/height can be omitted) const result = engine.processImage( videoElement, undefined, undefined, FrameType.Video ); // Method 4: Pass Uint8ClampedArray (width/height are required) const result = engine.processImage( uint8Array, 640, // required 480, // required FrameType.Video ); ``` ### Draw Processed Image ```javascript theme={null} // Draw processed image data to Canvas ctx.putImageData(result, 0, 0); ``` ### Save Processed Image Processed image data can be saved as image files: **Method 1: Using Canvas.toDataURL()** ```javascript theme={null} // Draw processed image data to Canvas ctx.putImageData(result, 0, 0); // Convert to Data URL const dataURL = canvas.toDataURL('image/png'); // Create download link const link = document.createElement('a'); link.download = 'beauty-result.png'; link.href = dataURL; link.click(); ``` **Method 2: Using Canvas.toBlob()** ```javascript theme={null} // Draw processed image data to Canvas ctx.putImageData(result, 0, 0); // Convert to Blob canvas.toBlob(function(blob) { // Create download link const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.download = 'beauty-result.png'; link.href = url; link.click(); // Release URL object URL.revokeObjectURL(url); }, 'image/png'); ``` **Method 3: Save as Different Formats** ```javascript theme={null} // Save as PNG (lossless, larger file size) const pngDataURL = canvas.toDataURL('image/png'); // Save as JPEG (lossy, smaller file size, quality can be set) const jpegDataURL = canvas.toDataURL('image/jpeg', 0.9); // Quality 0-1 // Create download function downloadImage(dataURL, filename, mimeType) { const link = document.createElement('a'); link.download = filename; link.href = dataURL; link.click(); } // Usage downloadImage(pngDataURL, 'beauty-result.png', 'image/png'); downloadImage(jpegDataURL, 'beauty-result.jpg', 'image/jpeg'); ``` **Complete Example: Save Processed Image** ```javascript theme={null} function processAndSaveImage(imageData, engine) { try { // Process image (synchronous method) const result = engine.processImage( imageData, imageData.width, imageData.height, FrameType.Image ); // Create temporary Canvas const canvas = document.createElement('canvas'); canvas.width = imageData.width; canvas.height = imageData.height; const ctx = canvas.getContext('2d'); // Draw processed image ctx.putImageData(result, 0, 0); // Save as image canvas.toBlob(function(blob) { const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.download = `beauty-${Date.now()}.png`; link.href = url; link.click(); URL.revokeObjectURL(url); }, 'image/png'); console.log('Image saved successfully'); } catch (error) { console.error('Failed to process or save image:', error); } } ``` ## Lifecycle Management Call `destroy()` method after engine use to release resources and avoid memory leaks. ### Release Resources When page unloads or no longer in use, be sure to release engine resources: ```javascript theme={null} // On page unload window.addEventListener('beforeunload', () => { if (engine) { engine.destroy(); engine = null; } }); // Or in component destruction (Vue/React) onUnmounted(() => { // Vue if (engine) { engine.destroy(); engine = null; } }); useEffect(() => { // React return () => { if (engine) { engine.destroy(); engine = null; } }; }, []); ``` ### Memory Management * Release unused ImageData objects promptly * Avoid repeatedly creating large numbers of image objects in loops * Recommend reusing Canvas and ImageData objects ```javascript theme={null} // Reuse Canvas and ImageData let canvas = null; let imageData = null; function processFrame() { if (!canvas) { canvas = document.createElement('canvas'); canvas.width = 640; canvas.height = 480; } const ctx = canvas.getContext('2d'); // Draw image to canvas ctx.drawImage(video, 0, 0); // Get image data imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); // Process image (synchronous method) const result = engine.processImage( imageData, canvas.width, canvas.height, FrameType.Video ); // Draw result ctx.putImageData(result, 0, 0); } ``` ## Related Documentation * [Best Practices](/web/best-practices) - Performance optimization and architecture design recommendations * [FAQ](/web/faq) - Common questions and troubleshooting * [API Reference](/web/api-reference) - Complete API documentation # Quick Start Source: https://facebetter.mintlify.app/web/quick-start Run Web example ## Requirements * **Browser**: Modern browsers * Chrome 57+ * Firefox 52+ * Safari 11+ * Edge 16+ * **Language**: JavaScript (ES6+) * **Build Tools**: Optional (Vite, Webpack, Parcel, etc.) ## Install SDK Install via npm: ```bash theme={null} npm install facebetter ``` **Using ES Module (Recommended):** ```javascript theme={null} import { BeautyEffectEngine, EngineConfig, BeautyType, BasicParam, ProcessMode } from 'facebetter'; ``` **Using CommonJS:** ```javascript theme={null} const { BeautyEffectEngine, EngineConfig } = require('facebetter'); ``` ## Get Example Source Code Clone the [github repository](https://github.com/pixpark/facebetter-sdk) to local, navigate to `demo/web/vue` or `demo/web/react` directory ```bash theme={null} # clone git clone https://github.com/pixpark/facebetter-sdk.git # Vue example cd demo/web/vue # Or React example cd demo/web/react ``` ## Configure Application Information and Keys ### Bind Application Domain Follow the instructions on [this page](/intro/enable-service#bind-application-information) to bind your Web application domain in the console, for example: `example.com` ### Get AppID and AppKey Follow the instructions on [this page](/intro/enable-service#get-appid-and-appkey) to get `appId` and `appKey`, open `src/views/CameraPreview.vue` (Vue) or `src/views/CameraPreview.jsx` (React) and modify the configuration: ```javascript theme={null} const config = new EngineConfig({ appId: 'your appId', appKey: 'your appKey' // Optional: If licenseJson is provided, license data verification takes priority, appId and appKey are not required // licenseJson: 'your license json string' }) ``` `licenseJson` takes priority over `appId` + `appKey`. For offline download and details, see [License & Auth](/intro/license). ## Run Example Install dependencies and start the development server: ```bash theme={null} # Install dependencies npm install # Start development server npm run dev ``` Then access the displayed address in your browser (usually `http://localhost:5173`) ## Verify Running 1. Open browser developer tools (F12) 2. Check console for error messages 3. If you see "Engine initialized successfully", it's running correctly 4. You can click function buttons to test beauty effects # API Reference Source: https://facebetter.mintlify.app/windows/api-reference Complete Facebetter SDK C++ API reference for Windows and Linux > Applies to: **Windows** and **Linux**. All APIs are in the `facebetter` namespace.\ > SDK version: `1.3.1` *** ## Logging ### LogLevel ```cpp theme={null} enum class LogLevel { Trace = 0, Debug, Info, Warn, Error, Critical }; ``` ### LogConfig Log configuration struct. **Fields:** * `console_enabled` — write to stdout (default `false`) * `file_enabled` — write to a file (default `false`) * `level` — minimum log level to output (default `Info`) * `file_name` — log file path; only used when `file_enabled = true` (default `""`) ```cpp theme={null} struct LogConfig { bool console_enabled = false; bool file_enabled = false; LogLevel level = LogLevel::Info; std::string file_name = ""; }; ``` *** ## Engine ### EngineConfig Engine initialization config struct. **Fields:** * `app_id` — application ID from the dashboard * `app_key` — application key from the dashboard * `resource_path` — path to `resource.fbd` (relative or absolute) * `license_json` — offline/online license JSON string (optional) * `external_context` — whether the caller provides the OpenGL context (default `false`) **Authentication priority:** 1. If `license_json` is non-empty → use license JSON (supports both online response and offline license) 2. Otherwise → use `app_id` + `app_key` with network verification ```cpp theme={null} struct EngineConfig { std::string app_id; std::string app_key; std::string resource_path; std::string license_json; // optional bool external_context = false; }; ``` *** ### BeautyEffectEngine The main beauty engine class. Non-copyable, non-assignable. #### Static Methods ##### `SetLogConfig` ```cpp theme={null} static int SetLogConfig(const LogConfig& config); ``` Set the global SDK log configuration. **Must be called before `Create`** to capture initialization messages. | Parameter | Description | | --------- | ------------------------ | | `config` | Log configuration struct | Returns `0` on success, non-zero on failure. ```cpp theme={null} LogConfig log_cfg; log_cfg.console_enabled = true; log_cfg.level = LogLevel::Info; BeautyEffectEngine::SetLogConfig(log_cfg); ``` ##### `Create` ```cpp theme={null} static std::shared_ptr Create(const EngineConfig& config); ``` Create and initialize an engine instance. Returns `nullptr` on failure. ```cpp theme={null} EngineConfig cfg; cfg.app_id = "your_app_id"; cfg.app_key = "your_app_key"; cfg.resource_path = "resource/resource.fbd"; auto engine = BeautyEffectEngine::Create(cfg); if (!engine) { // Check log output for details } ``` *** #### Beauty Parameters ##### `SetBeautyParam` (Basic) ```cpp theme={null} virtual int SetBeautyParam(beauty_params::Basic param, float value) = 0; ``` Set a basic skin-beauty parameter. All values in `[0.0, 1.0]`; `0` disables the effect. | `param` | Description | | ------------------- | -------------- | | `Basic::Smoothing` | Skin smoothing | | `Basic::Sharpening` | Sharpening | | `Basic::Whitening` | Skin whitening | | `Basic::Rosiness` | Rosy tone | ```cpp theme={null} engine->SetBeautyParam(Basic::Smoothing, 0.5f); engine->SetBeautyParam(Basic::Whitening, 0.3f); engine->SetBeautyParam(Basic::Rosiness, 0.2f); engine->SetBeautyParam(Basic::Sharpening, 0.4f); ``` ##### `SetBeautyParam` (Reshape) ```cpp theme={null} virtual int SetBeautyParam(beauty_params::Reshape param, float value) = 0; ``` | `param` | Description | | ---------------------- | -------------------- | | `Reshape::FaceThin` | Face slimming | | `Reshape::FaceVShape` | V-face | | `Reshape::FaceNarrow` | Narrow face | | `Reshape::FaceShort` | Short face | | `Reshape::Cheekbone` | Cheekbone slimming | | `Reshape::Jawbone` | Jaw slimming | | `Reshape::Chin` | Chin slimming | | `Reshape::NoseSlim` | Nose bridge slimming | | `Reshape::EyeSize` | Eye enlarging | | `Reshape::EyeDistance` | Eye distance | ```cpp theme={null} engine->SetBeautyParam(Reshape::FaceThin, 0.4f); engine->SetBeautyParam(Reshape::EyeSize, 0.6f); ``` ##### `SetBeautyParam` (Makeup) ```cpp theme={null} virtual int SetBeautyParam(beauty_params::Makeup param, float value) = 0; ``` | `param` | Description | | ------------------ | ----------- | | `Makeup::Lipstick` | Lipstick | | `Makeup::Blush` | Blush | ```cpp theme={null} engine->SetBeautyParam(Makeup::Lipstick, 0.7f); engine->SetBeautyParam(Makeup::Blush, 0.4f); ``` ##### `SetLipstickStyle` / `SetBlushStyle` ```cpp theme={null} virtual int SetLipstickStyle(beauty_params::LipstickStyle style) = 0; virtual int SetBlushStyle(beauty_params::BlushStyle style) = 0; ``` | Enum | Value | Description | | ---------------------- | ----- | ----------------- | | `LipstickStyle::Rouge` | 0 | Rose red | | `LipstickStyle::Coral` | 1 | Coral | | `LipstickStyle::Pink` | 2 | Pink (default) | | `BlushStyle::Classic` | 0 | Classic (default) | | `BlushStyle::Peach` | 1 | Peach | | `BlushStyle::Rose` | 2 | Rose | ```cpp theme={null} engine->SetLipstickStyle(LipstickStyle::Rouge); engine->SetBlushStyle(BlushStyle::Peach); ``` ##### `SetBeautyParam` (ChromaKey) ```cpp theme={null} virtual int SetBeautyParam(beauty_params::ChromaKey param, float value) = 0; ``` | `param` | Description | | ------------------------- | --------------------------------------- | | `ChromaKey::KeyColor` | Key color: `0`=Green, `1`=Blue, `2`=Red | | `ChromaKey::Similarity` | Similarity threshold `[0.0, 1.0]` | | `ChromaKey::Smoothness` | Edge smoothness `[0.0, 1.0]` | | `ChromaKey::Desaturation` | Desaturation `[0.0, 1.0]` | ```cpp theme={null} engine->SetBeautyParam(ChromaKey::KeyColor, 0.0f); // green engine->SetBeautyParam(ChromaKey::Similarity, 0.4f); engine->SetBeautyParam(ChromaKey::Smoothness, 0.1f); ``` ##### `SetVirtualBackground` ```cpp theme={null} virtual int SetVirtualBackground( const beauty_params::VirtualBackgroundOptions& options) = 0; ``` Configure virtual background. ```cpp theme={null} // Background blur beauty_params::VirtualBackgroundOptions opts; opts.mode = beauty_params::BackgroundMode::Blur; engine->SetVirtualBackground(opts); // Background image replacement opts.mode = beauty_params::BackgroundMode::Image; opts.background_image = bg_frame; // std::shared_ptr engine->SetVirtualBackground(opts); // Disable opts.mode = beauty_params::BackgroundMode::None; engine->SetVirtualBackground(opts); ``` ##### `SetSkinOnlyBeauty` ```cpp theme={null} virtual int SetSkinOnlyBeauty(bool enabled) = 0; ``` Set whether beauty effects are applied only to skin regions. When enabled, beauty effects (smoothing, whitening, etc.) will only be applied to detected skin areas, leaving non-skin areas unchanged. | Parameter | Description | | --------- | ------------------------------------------------------------------- | | `enabled` | `true` to enable skin-only beauty, `false` to apply to entire image | ```cpp theme={null} // Enable skin-only beauty engine->SetSkinOnlyBeauty(true); // Disable skin-only beauty engine->SetSkinOnlyBeauty(false); ``` *** #### Filter Management ##### `SetFilter` ```cpp theme={null} virtual int SetFilter(const std::string& filter_id) = 0; ``` Apply a LUT filter by ID. Pass `""` to clear. ```cpp theme={null} engine->SetFilter("chuxin"); // apply engine->SetFilter(""); // clear ``` ##### `SetFilterIntensity` ```cpp theme={null} virtual int SetFilterIntensity(float intensity) = 0; ``` Set current filter intensity `[0.0, 1.0]`. ##### `RegisterFilter` ```cpp theme={null} virtual int RegisterFilter(const std::string& filter_id, const std::string& fbd_file_path) = 0; virtual int RegisterFilter(const std::string& filter_id, const std::vector& fbd_data) = 0; ``` Register a custom filter from a `.fbd` file path or memory buffer. ```cpp theme={null} engine->RegisterFilter("my_filter", "assets/filters/my_filter.fbd"); engine->SetFilter("my_filter"); ``` ##### `UnregisterFilter` / `UnregisterAllFilters` ```cpp theme={null} virtual int UnregisterFilter(const std::string& filter_id) = 0; virtual int UnregisterAllFilters() = 0; ``` ##### `GetRegisteredFilters` ```cpp theme={null} virtual std::vector GetRegisteredFilters() const = 0; ``` *** #### Sticker Management ##### `SetSticker` ```cpp theme={null} virtual int SetSticker(const std::string& sticker_id) = 0; ``` Apply a sticker by ID. Pass `""` to clear. ```cpp theme={null} engine->SetSticker("rabbit"); engine->SetSticker(""); ``` ##### `RegisterSticker` ```cpp theme={null} virtual int RegisterSticker(const std::string& sticker_id, const std::string& fbd_file_path) = 0; virtual int RegisterSticker(const std::string& sticker_id, const std::vector& fbd_data) = 0; ``` Register a custom sticker from a `.fbd` file path or memory buffer. ##### `UnregisterSticker` / `UnregisterAllStickers` ```cpp theme={null} virtual int UnregisterSticker(const std::string& sticker_id) = 0; virtual int UnregisterAllStickers() = 0; ``` ##### `GetRegisteredStickers` ```cpp theme={null} virtual std::vector GetRegisteredStickers() const = 0; ``` *** #### Callbacks ##### `SetCallbacks` ```cpp theme={null} virtual int SetCallbacks(const EngineCallbacks& callbacks) = 0; ``` Register engine event and data callbacks. | Callback field | Type | Description | | ------------------- | ---------------------------------------------------- | --------------------------------------- | | `on_face_landmarks` | `function&)>` | Face landmark data, fired every frame | | `on_engine_event` | `function` | Engine events (auth, init status, etc.) | ```cpp theme={null} EngineCallbacks cbs; cbs.on_face_landmarks = [](const std::vector& faces) { for (const auto& face : faces) { printf("face_id=%d score=%.2f keypoints=%zu\n", face.face_id, face.score, face.key_points.size()); } }; cbs.on_engine_event = [](int code, const std::string& msg) { if (code == 0) printf("[Engine] %s\n", msg.c_str()); else fprintf(stderr, "[Engine] error %d: %s\n", code, msg.c_str()); }; engine->SetCallbacks(cbs); ``` *** #### Image Processing ##### `ProcessImage` ```cpp theme={null} virtual const std::shared_ptr ProcessImage( const std::shared_ptr image_frame) = 0; ``` Apply all configured beauty effects to the input frame and return the processed frame. Returns `nullptr` on failure. The engine tries to keep the output format consistent with the input format. ```cpp theme={null} auto input = ImageFrame::CreateWithRGBA(data, width, height, stride); input->type = FrameType::Video; auto output = engine->ProcessImage(input); if (output && output->Data()) { // success } ``` *** #### Deprecated APIs **Deprecated** The following methods are no-ops in parameter-driven mode. They are retained for binary compatibility only and will be removed in a future release. | Method | Behavior | | ---------------------------------------- | ---------------------- | | `SetBeautyTypeEnabled(BeautyType, bool)` | No-op | | `IsBeautyTypeEnabled(BeautyType) const` | Always returns `false` | | `DisableAllBeautyTypes()` | No-op | *** ## Image ### Format ```cpp theme={null} enum class Format { I420, // YUV 4:2:0, 12bpp, 3 planes (Y, U, V) NV12, // YUV 4:2:0, 12bpp, 2 planes (Y + interleaved UV) NV21, // YUV 4:2:0, 12bpp, 2 planes (Y + interleaved VU, Android default) BGRA, // 32bpp, 4 channels RGBA, // 32bpp, 4 channels BGR, // 24bpp, 3 channels RGB, // 24bpp, 3 channels Texture, // GPU texture (OpenGL) }; ``` ### Rotation ```cpp theme={null} enum class Rotation { Rotation_0, // No rotation Rotation_90, // 90° clockwise Rotation_180, // 180° clockwise Rotation_270, // 270° clockwise }; ``` ### FrameType Set via `frame->type`. Controls how the engine processes each frame. ```cpp theme={null} enum class FrameType { Image = 0, // Photo mode: full detection per frame Video = 1 // Video mode: temporal optimization (default) }; ``` *** ### ImageFrame Image frame class. Non-copyable. **Thread safety:** `ImageFrame` is not thread-safe. Do not call `Rotate`, `Mirror`, `Convert`, etc. on the same instance from multiple threads simultaneously. #### Factory Methods ##### `CreateWithFile` ```cpp theme={null} static std::shared_ptr CreateWithFile(const std::string& file_path); ``` Load from a JPEG, PNG, or BMP file. Returns `nullptr` on failure. ##### `Create` (general) ```cpp theme={null} static std::shared_ptr Create( const uint8_t* data, int width, int height, Format format); ``` Create from memory for single-plane formats (RGBA / BGRA / RGB / BGR). > For multi-plane YUV (I420/NV12/NV21), use the dedicated methods instead. ##### `CreateWithRGBA` ```cpp theme={null} static std::shared_ptr CreateWithRGBA( const uint8_t* data, int width, int height, int stride); ``` ```cpp theme={null} auto frame = ImageFrame::CreateWithRGBA(rgba_ptr, 1280, 720, 1280 * 4); ``` ##### `CreateWithBGRA` ```cpp theme={null} static std::shared_ptr CreateWithBGRA( const uint8_t* data, int width, int height, int stride); ``` ##### `CreateWithRGB` ```cpp theme={null} static std::shared_ptr CreateWithRGB( const uint8_t* data, int width, int height, int stride); ``` ##### `CreateWithBGR` ```cpp theme={null} static std::shared_ptr CreateWithBGR( const uint8_t* data, int width, int height, int stride); ``` ##### `CreateWithI420` ```cpp theme={null} static std::shared_ptr CreateWithI420( int width, int height, const uint8_t* dataY, int strideY, const uint8_t* dataU, int strideU, const uint8_t* dataV, int strideV); ``` ```cpp theme={null} auto frame = ImageFrame::CreateWithI420( 1280, 720, y_ptr, y_stride, u_ptr, u_stride, v_ptr, v_stride); ``` ##### `CreateWithNV12` ```cpp theme={null} static std::shared_ptr CreateWithNV12( int width, int height, const uint8_t* dataY, int strideY, const uint8_t* dataUV, int strideUV); ``` ##### `CreateWithNV21` ```cpp theme={null} static std::shared_ptr CreateWithNV21( int width, int height, const uint8_t* dataY, int strideY, const uint8_t* dataUV, int strideUV); ``` ##### `CreateWithTexture` ```cpp theme={null} static std::shared_ptr CreateWithTexture( uint32_t texture, int width, int height, int stride); ``` Create from an OpenGL texture handle. Used for GPU texture input. ```cpp theme={null} auto frame = ImageFrame::CreateWithTexture(gl_tex_id, 1280, 720, 0); ``` ##### `CreateWithAndroid420` ```cpp theme={null} static std::shared_ptr CreateWithAndroid420( int width, int height, uint8_t* yBuffer, int strideY, uint8_t* uBuffer, int strideU, uint8_t* vBuffer, int strideV, int pixelStrideUV); ``` For Android Camera2 `YUV_420_888` (primarily used on Android). *** #### Instance Methods ##### `Rotate` ```cpp theme={null} int Rotate(Rotation rotation); ``` Rotate the frame in-place. Returns `0` on success. ```cpp theme={null} frame->Rotate(Rotation::Rotation_90); ``` ##### `Mirror` ```cpp theme={null} int Mirror(const std::string& mode); ``` Mirror the frame in-place. `mode` values (case-insensitive): `"horizontal"`, `"vertical"`, `"both"`. ```cpp theme={null} frame->Mirror("horizontal"); ``` ##### `SetMirror` ```cpp theme={null} void SetMirror(const std::string& mode); ``` Set a mirror flag. The engine applies the mirror during `ProcessImage` without an extra format round-trip. Pass `""` to clear. ```cpp theme={null} frame->SetMirror("horizontal"); ``` ##### `Convert` ```cpp theme={null} std::shared_ptr Convert(Format format) const; ``` Convert to another pixel format; returns a new `ImageFrame`. If the target format matches the current format, the returned frame shares the underlying buffer. Returns `nullptr` if conversion is not possible. ```cpp theme={null} auto rgba = frame->Convert(Format::RGBA); auto i420 = frame->Convert(Format::I420); ``` ##### `ToFile` ```cpp theme={null} int ToFile(const std::string& path, int quality = 90) const; ``` Save the frame to a file. `quality` is the JPEG compression quality (`1`–`100`); ignored for PNG. Returns `0` on success. ```cpp theme={null} output->ToFile("output.jpg", 95); output->ToFile("output.png"); ``` *** #### Data Accessors | Method | Description | | -------------------------- | --------------------------------------------------------------- | | `Width() const` | Image width in pixels | | `Height() const` | Image height in pixels | | `Stride() const` | Row stride in bytes | | `Size() const` | Total pixel data size in bytes | | `GetFormat() const` | Pixel format enum | | `Data() const` | Raw pixel pointer (interleaved formats) | | `DataY() const` | Y plane pointer (I420) | | `DataU() const` | U plane pointer (I420) | | `DataV() const` | V plane pointer (I420) | | `StrideY() const` | Y plane stride (I420) | | `StrideU() const` | U plane stride (I420) | | `StrideV() const` | V plane stride (I420) | | `DataUV() const` | UV plane pointer (NV12/NV21) | | `StrideUV() const` | UV plane stride (NV12/NV21) | | `Texture() const` | OpenGL texture handle (`Texture` format); returns `0` otherwise | | `Buffer() const` | Underlying `ImageBuffer` smart pointer (advanced use) | | `MirrorHorizontal() const` | Whether horizontal mirror flag is set | | `MirrorVertical() const` | Whether vertical mirror flag is set | #### Public Field | Field | Type | Default | Description | | ------ | ----------- | ------------------ | --------------------------------------------- | | `type` | `FrameType` | `FrameType::Video` | Processing mode: video stream or single photo | *** ## Callbacks and Events ### EngineEventCode ```cpp theme={null} enum class EngineEventCode { LicenseValidationSuccess = 0, // License validated successfully LicenseValidationFailed = 1, // Validation failed (check message) EngineInitializationComplete = 100, // Engine ready, filters loaded EngineInitializationFailed = 101, // Initialization failed }; ``` ### EngineCallbacks ```cpp theme={null} struct EngineCallbacks { // Fired every frame; results may be empty if no face is detected std::function& results)> on_face_landmarks = nullptr; // Fired on auth and initialization events std::function on_engine_event = nullptr; }; ``` *** ## Data Structures ### Point2d ```cpp theme={null} struct Point2d { float x; // Normalized X [0.0, 1.0] float y; // Normalized Y [0.0, 1.0] }; ``` ### Rect ```cpp theme={null} struct Rect { float x; // Top-left X (normalized) float y; // Top-left Y (normalized) float width; // Width (normalized) float height; // Height (normalized) }; ``` ### FaceDetectionResult Single face detection result. | Field | Type | Description | | ------------- | ----------------- | ------------------------------------------------------------------ | | `rect` | `Rect` | Face ROI bounding box (normalized) | | `key_points` | `vector` | 111 normalized face landmark coordinates | | `visibility` | `vector` | Per-keypoint visibility scores `[0.0, 1.0]` | | `face_id` | `int` | Unique face ID for cross-frame tracking; `-1` if unknown | | `face_action` | `int` | Face action bitmask (e.g., mouth open, eye blink); `-1` if unknown | | `score` | `float` | Face detection confidence `[0.0, 1.0]` | | `pitch` | `float` | Pitch angle (up=negative, down=positive) in `[-π, π]` | | `roll` | `float` | Roll angle (left=negative, right=positive) in `[-π, π]` | | `yaw` | `float` | Yaw angle (left=negative, right=positive) in `[-π, π]` | ```cpp theme={null} struct FaceDetectionResult { Rect rect; std::vector key_points; std::vector visibility; int face_id = -1; int face_action = -1; float score = 0.0f; float pitch = 0.0f; float roll = 0.0f; float yaw = 0.0f; }; ``` **Example — reading keypoints:** ```cpp theme={null} cbs.on_face_landmarks = [](const std::vector& faces) { for (const auto& face : faces) { printf("face_id=%d score=%.2f keypoints=%zu\n", face.face_id, face.score, face.key_points.size()); if (!face.key_points.empty()) { const auto& pt = face.key_points[0]; printf(" point[0] = (%.3f, %.3f)\n", pt.x, pt.y); } } }; ``` *** ## Beauty Param Enumerations ### BeautyType ```cpp theme={null} enum class BeautyType { Basic = 0, Reshape, Makeup, VirtualBackground, ChromaKey, Filter, Sticker, }; ``` ### beauty\_params::Basic ```cpp theme={null} enum class Basic { Smoothing = 0, Sharpening, Whitening, Rosiness, }; ``` ### beauty\_params::Reshape ```cpp theme={null} enum class Reshape { FaceThin = 0, FaceVShape, FaceNarrow, FaceShort, Cheekbone, Jawbone, Chin, NoseSlim, EyeSize, EyeDistance, }; ``` ### beauty\_params::Makeup ```cpp theme={null} enum class Makeup { Lipstick, Blush, }; ``` ### beauty\_params::ChromaKey ```cpp theme={null} enum class ChromaKey { KeyColor = 0, // 0=Green, 1=Blue, 2=Red Similarity, Smoothness, Desaturation, }; ``` ### beauty\_params::BackgroundMode ```cpp theme={null} enum class BackgroundMode { None = 0, Blur, Image, }; ``` ### beauty\_params::VirtualBackgroundOptions ```cpp theme={null} struct VirtualBackgroundOptions { BackgroundMode mode = BackgroundMode::None; std::shared_ptr background_image = nullptr; VirtualBackgroundOptions() = default; explicit VirtualBackgroundOptions(BackgroundMode m) : mode(m) {} }; ``` *** ## Notes **Thread safety:** `BeautyEffectEngine` and `ImageFrame` are not thread-safe. Add external locking for multi-threaded use. **Memory management:** All factory methods return `std::shared_ptr`. Memory is managed automatically. **Read-only data:** Pointers returned by `Data()` etc. are `const`. Do not write to them; create a new `ImageFrame` if you need to modify pixel data. **`SetRenderView` not available:** This method exists only on iOS and macOS. Do not call it on Windows or Linux. # Best Practices Source: https://facebetter.mintlify.app/windows/best-practices Best practices and performance optimization for Windows beauty SDK # Error Handling Source: https://facebetter.mintlify.app/windows/error-handling Facebetter SDK error handling and troubleshooting guide for Windows ## Return Value Convention Most SDK methods return `int`: | Return value | Meaning | | ------------ | ------------------------------------------ | | `0` | Success | | Non-zero | Failure — check the log output for details | `BeautyEffectEngine::Create()` returns a `std::shared_ptr`; it returns `nullptr` on failure. *** ## Common Errors and Fixes ### 1. Engine Creation Fails (`Create` Returns `nullptr`) **Possible causes:** * Invalid or inactive `app_id` / `app_key` * `resource.fbd` file not found at `resource_path` * `facebetter.dll` missing or version mismatch * No network access (online authentication failed) **How to diagnose:** ```cpp theme={null} // Enable logging BEFORE calling Create LogConfig log_cfg; log_cfg.console_enabled = true; log_cfg.level = LogLevel::Debug; BeautyEffectEngine::SetLogConfig(log_cfg); auto engine = BeautyEffectEngine::Create(eng_cfg); if (!engine) { std::cerr << "[Error] Engine creation failed. Check log output." << std::endl; return -1; } ``` **Common fixes:** 1. Use an absolute path to rule out relative path issues: ```cpp theme={null} #include eng_cfg.resource_path = (std::filesystem::current_path() / "resource" / "resource.fbd").string(); ``` 2. Make sure `facebetter.dll` is in the same directory as the executable (CMake copies it automatically after build). 3. Get fresh `app_id` and `app_key` from the dashboard. *** ### 2. `facebetter.dll` Not Found at Runtime **Symptom:** A Windows dialog reports "facebetter.dll was not found" on startup. **Fix:** ```bat theme={null} :: Check whether the DLL is next to the executable dir build\facebetter.dll :: If missing, copy manually copy sdk\lib\facebetter.dll build\ ``` Or re-run `cmake --build build` — CMake automatically copies the DLL after every build. *** ### 3. `ProcessImage` Returns `nullptr` **Possible causes:** * Input `ImageFrame` is null (file not found, unsupported format, etc.) * Engine was not initialized successfully * Out of memory **How to handle:** ```cpp theme={null} auto input = ImageFrame::CreateWithFile("input.jpg"); if (!input || !input->Data()) { std::cerr << "[Error] Failed to load input image." << std::endl; return; } auto output = engine->ProcessImage(input); if (!output || !output->Data()) { std::cerr << "[Error] ProcessImage failed." << std::endl; return; } ``` *** ### 4. Beauty Effects Have No Visible Impact **Possible causes:** * The relevant `BeautyType` was not enabled via `SetBeautyTypeEnabled` * Parameter value is `0` (effect is off) * No face detected in the frame (reshape/makeup require face detection to succeed) **Checklist:** ```cpp theme={null} // Enable each type you need engine->SetBeautyTypeEnabled(BeautyType::Basic, true); engine->SetBeautyTypeEnabled(BeautyType::Reshape, true); engine->SetBeautyTypeEnabled(BeautyType::Makeup, true); engine->SetBeautyTypeEnabled(BeautyType::Sticker, true); // Ensure the parameter value is greater than 0 engine->SetBeautyParam(Basic::Smoothing, 0.5f); // 0 = disabled ``` *** ### 5. `SetBeautyParam` Returns Non-Zero **Possible causes:** * `engine` is `nullptr` (engine creation failed earlier) * Value out of range `[0.0, 1.0]` **How to handle:** ```cpp theme={null} int ret = engine->SetBeautyParam(Basic::Smoothing, 0.5f); if (ret != 0) { std::cerr << "[Error] SetBeautyParam failed, code: " << ret << std::endl; } ``` *** ### 6. OpenGL / Display Issues **Possible causes:** * GLFW initialization failed (driver issue or OpenGL 3.0+ not available) * `gladLoadGLLoader` returned false **How to diagnose:** ```cpp theme={null} if (!glfwInit()) { std::cerr << "[Error] GLFW init failed." << std::endl; return 1; } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); ``` Make sure your GPU driver is up to date. Hardware OpenGL is usually unavailable in virtual machines or Remote Desktop sessions. *** ## Enabling Debug Logs During development, enable `Debug`-level logging for maximum detail: ```cpp theme={null} LogConfig log_cfg; log_cfg.console_enabled = true; log_cfg.file_enabled = true; log_cfg.level = LogLevel::Debug; log_cfg.file_name = "facebetter.log"; BeautyEffectEngine::SetLogConfig(log_cfg); ``` The log file is written to the same directory as the executable. *** ## Error Code Reference | Code | Meaning | | ---- | ----------------------- | | `0` | Success | | `-1` | Engine not initialized | | `-2` | Invalid parameter | | `-3` | Resource file not found | | `-4` | Processing failed | # FAQ Source: https://facebetter.mintlify.app/windows/faq Common questions and solutions for Windows beauty SDK integration # Implement Beauty Source: https://facebetter.mintlify.app/windows/implement-beauty Integrate the Facebetter SDK on Windows using the C++ interface On Windows, the Facebetter SDK is accessed through its C++ interface. This guide follows the [C++ Desktop Demo](https://github.com/pixpark/facebetter-sdk) as a reference for the complete integration flow. ## Include Headers ```cpp theme={null} #include #include #include #include using namespace facebetter; using namespace facebetter::beauty_params; ``` ## Integration Overview ``` Configure logging → Create engine → Enable beauty types → Set params → Process frames → Render result ``` *** ## 1. Configure Logging (optional) Call this before creating the engine so that initialization log messages are captured. ```cpp theme={null} LogConfig log_cfg; log_cfg.console_enabled = true; // print to console log_cfg.file_enabled = false; // no log file log_cfg.level = LogLevel::Info; BeautyEffectEngine::SetLogConfig(log_cfg); ``` Log levels (ascending): `Trace` / `Debug` / `Info` / `Warn` / `Error` / `Critical`. *** ## 2. Create the Engine ```cpp theme={null} EngineConfig eng_cfg; eng_cfg.app_id = "your_app_id"; eng_cfg.app_key = "your_app_key"; eng_cfg.resource_path = "resource/resource.fbd"; // relative or absolute path eng_cfg.external_context = false; // SDK manages its own OpenGL context std::shared_ptr engine = BeautyEffectEngine::Create(eng_cfg); if (!engine) { // Check app_id / app_key / resource_path return -1; } ``` > `resource_path` must point to the `resource.fbd` file shipped with the SDK, which contains AI models and filter assets. *** ## 3. Enable Beauty Types All beauty types are **disabled by default** and must be explicitly enabled: ```cpp theme={null} engine->SetBeautyTypeEnabled(BeautyType::Basic, true); // skin beauty engine->SetBeautyTypeEnabled(BeautyType::Reshape, true); // face reshaping engine->SetBeautyTypeEnabled(BeautyType::Makeup, true); // makeup engine->SetBeautyTypeEnabled(BeautyType::Sticker, true); // stickers ``` *** ## 4. Set Beauty Parameters All parameter values are in the range `[0.0, 1.0]`. A value of `0` disables the effect. ### Basic Beauty ```cpp theme={null} engine->SetBeautyParam(Basic::Smoothing, 0.5f); // smoothing engine->SetBeautyParam(Basic::Whitening, 0.3f); // whitening engine->SetBeautyParam(Basic::Rosiness, 0.2f); // rosy tone engine->SetBeautyParam(Basic::Sharpening, 0.4f); // sharpening ``` ### Skin-Only Beauty Use `SetSkinOnlyBeauty` to set whether beauty effects are applied only to skin regions. When enabled, beauty effects (smoothing, whitening, etc.) will only be applied to detected skin areas, leaving non-skin areas unchanged. ```cpp theme={null} // Enable skin-only beauty engine->SetSkinOnlyBeauty(true); // Disable skin-only beauty (apply to entire image) engine->SetSkinOnlyBeauty(false); ``` After enabling skin-only beauty, even with high beauty parameter values, non-skin areas (such as background, clothing, etc.) will not be affected. ### Face Reshape ```cpp theme={null} engine->SetBeautyParam(Reshape::FaceThin, 0.4f); // face slimming engine->SetBeautyParam(Reshape::FaceVShape, 0.3f); // V-face engine->SetBeautyParam(Reshape::FaceNarrow, 0.2f); // narrow face engine->SetBeautyParam(Reshape::FaceShort, 0.2f); // short face engine->SetBeautyParam(Reshape::Cheekbone, 0.3f); // cheekbone slimming engine->SetBeautyParam(Reshape::Jawbone, 0.2f); // jaw slimming engine->SetBeautyParam(Reshape::Chin, 0.2f); // chin slimming engine->SetBeautyParam(Reshape::NoseSlim, 0.3f); // nose bridge slimming engine->SetBeautyParam(Reshape::EyeSize, 0.4f); // eye enlarging engine->SetBeautyParam(Reshape::EyeDistance, 0.1f); // eye distance ``` ### Makeup ```cpp theme={null} engine->SetBeautyParam(Makeup::Lipstick, 0.6f); // lipstick engine->SetBeautyParam(Makeup::Blush, 0.4f); // blush engine->SetLipstickStyle(LipstickStyle::Rouge); // lipstick style engine->SetBlushStyle(BlushStyle::Classic); // blush style ``` ### Stickers Pass a sticker ID string to activate; pass an empty string to disable. Built-in sticker IDs are bundled in `resource.fbd`: ```cpp theme={null} engine->SetSticker("rabbit"); // enable the "rabbit" sticker engine->SetSticker(""); // disable sticker ``` ### Filters (LUT) ```cpp theme={null} engine->SetFilter("chuxin"); // apply filter engine->SetFilterIntensity(0.8f); // filter intensity engine->SetFilter(""); // disable filter ``` *** ## 5. Process Frames Typical per-frame processing: ```cpp theme={null} // Create an input frame from in-memory RGBA data (video stream) auto input_frame = ImageFrame::CreateWithRGBA( rgba_data, width, height, stride); // Set frame type: Video (live stream) or Image (single photo) input_frame->type = FrameType::Video; // Process auto output_frame = engine->ProcessImage(input_frame); if (output_frame && output_frame->Data()) { int w = output_frame->Width(); int h = output_frame->Height(); const uint8_t* p = output_frame->Data(); // RGBA, same format as input } ``` **Load from file** (single-image mode): ```cpp theme={null} auto input_frame = ImageFrame::CreateWithFile("input.jpg"); input_frame->type = FrameType::Image; auto output_frame = engine->ProcessImage(input_frame); ``` **Convert output format** if needed: ```cpp theme={null} auto bgra_frame = output_frame->Convert(Format::BGRA); auto i420_frame = output_frame->Convert(Format::I420); ``` *** ## 6. OpenGL Integration (render preview) Upload the processed RGBA data to an OpenGL texture: ```cpp theme={null} // Create texture (once) GLuint tex; glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Upload per frame glBindTexture(GL_TEXTURE_2D, tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, output_frame->Width(), output_frame->Height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, output_frame->Data()); // Render with ImGui or a custom quad ImGui::Image( static_cast(static_cast(tex)), ImVec2(img_w, img_h)); ``` *** ## 7. Full Example (real-time processing loop) The snippet below is drawn directly from the demo and shows the complete processing loop with frame-rate limiting: ```cpp theme={null} #include #include #include #include #include using namespace facebetter; using namespace facebetter::beauty_params; // --- Initialization --- LogConfig log_cfg; log_cfg.console_enabled = true; log_cfg.level = LogLevel::Info; BeautyEffectEngine::SetLogConfig(log_cfg); EngineConfig eng_cfg; eng_cfg.app_id = "your_app_id"; eng_cfg.app_key = "your_app_key"; eng_cfg.resource_path = "resource/resource.fbd"; eng_cfg.external_context = false; auto engine = BeautyEffectEngine::Create(eng_cfg); engine->SetBeautyTypeEnabled(BeautyType::Basic, true); engine->SetBeautyTypeEnabled(BeautyType::Reshape, true); engine->SetBeautyTypeEnabled(BeautyType::Makeup, true); engine->SetBeautyTypeEnabled(BeautyType::Sticker, true); engine->SetBeautyParam(Basic::Smoothing, 0.5f); engine->SetBeautyParam(Reshape::FaceThin, 0.4f); engine->SetBeautyParam(Makeup::Lipstick, 0.6f); // --- Processing loop (~30 fps) --- double last_time = 0.0; const double kInterval = 1.0 / 30.0; GLuint preview_tex = 0; while (!glfwWindowShouldClose(window)) { glfwPollEvents(); double now = glfwGetTime(); if (now - last_time >= kInterval) { auto input = ImageFrame::CreateWithRGBA( rgba_buffer, width, height, stride); input->type = FrameType::Video; auto output = engine->ProcessImage(input); if (output && output->Data()) { if (preview_tex == 0) { glGenTextures(1, &preview_tex); glBindTexture(GL_TEXTURE_2D, preview_tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } glBindTexture(GL_TEXTURE_2D, preview_tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, output->Width(), output->Height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, output->Data()); } last_time = now; } // ... ImGui render ... } // --- Cleanup --- if (preview_tex) glDeleteTextures(1, &preview_tex); engine.reset(); ``` *** ## Quick Reference ### BeautyType | Value | Description | | --------------------- | -------------- | | `BeautyType::Basic` | Skin beauty | | `BeautyType::Reshape` | Face reshaping | | `BeautyType::Makeup` | Makeup | | `BeautyType::Sticker` | Stickers | ### Basic Parameters | Parameter | Description | | ------------------- | -------------- | | `Basic::Smoothing` | Skin smoothing | | `Basic::Whitening` | Skin whitening | | `Basic::Rosiness` | Rosy tone | | `Basic::Sharpening` | Sharpening | ### Skin-Only Beauty | Method | Description | | -------------------------- | ------------------------ | | `SetSkinOnlyBeauty(true)` | Enable skin-only beauty | | `SetSkinOnlyBeauty(false)` | Disable skin-only beauty | ### Reshape Parameters | Parameter | Description | | ---------------------- | -------------------- | | `Reshape::FaceThin` | Face slimming | | `Reshape::FaceVShape` | V-face | | `Reshape::FaceNarrow` | Narrow face | | `Reshape::FaceShort` | Short face | | `Reshape::Cheekbone` | Cheekbone slimming | | `Reshape::Jawbone` | Jaw slimming | | `Reshape::Chin` | Chin slimming | | `Reshape::NoseSlim` | Nose bridge slimming | | `Reshape::EyeSize` | Eye enlarging | | `Reshape::EyeDistance` | Eye distance | ### Makeup Parameters | Parameter | Description | | ------------------ | ----------- | | `Makeup::Lipstick` | Lipstick | | `Makeup::Blush` | Blush | ### ImageFrame Factory Methods | Method | Description | | ------------------------------------------------------- | --------------------------------------------- | | `ImageFrame::CreateWithFile(path)` | Load from file (JPEG/PNG/BMP) | | `ImageFrame::CreateWithRGBA(data, w, h, stride)` | Create from in-memory RGBA data | | `ImageFrame::CreateWithBGRA(data, w, h, stride)` | Create from in-memory BGRA data | | `ImageFrame::CreateWithRGB(data, w, h, stride)` | Create from in-memory RGB data | | `ImageFrame::CreateWithI420(w, h, y, sy, u, su, v, sv)` | Create from I420 YUV data | | `ImageFrame::CreateWithNV12(w, h, y, sy, uv, suv)` | Create from NV12 data | | `frame->Convert(Format::RGBA)` | Convert to another format (returns new frame) | # Quick Start Source: https://facebetter.mintlify.app/windows/quick-start Build and run the Facebetter C++ desktop demo on Windows (GLFW + ImGui) This guide explains how to build and run the Facebetter C++ desktop demo on Windows. The demo is built with **GLFW + Dear ImGui + OpenGL 3**: the left panel shows a live beauty-processed preview, while the right panel provides sliders to control each effect in real time. ## Requirements | Tool | Version | | -------------------- | ------------------------------------------------------- | | Visual Studio / MSVC | 2019 or later (C++17 support required) | | CMake | 3.16+ | | Ninja | Any recent version (`winget install Ninja-build.Ninja`) | | Windows SDK | 10.0+ | > MinGW-w64 also works, but MSVC is recommended. ## Step 1: Clone the Repository ```bash theme={null} git clone https://github.com/pixpark/facebetter-sdk.git cd facebetter-sdk ``` ## Step 2: Place the SDK Files Unzip the Windows SDK package and place files under `demo/cpp/sdk/`: ``` demo/cpp/sdk/ ├── include/ │ └── facebetter/ │ ├── beauty_effect_engine.h │ ├── beauty_params.h │ ├── image_frame.h │ └── type_defines.h ├── lib/ │ ├── facebetter.lib ← import library │ └── facebetter.dll ← runtime DLL └── resource/ └── resource.fbd ← model and resource pack ``` The SDK download link is available on the [Download](https://facebetter.net/download) page or in your dashboard. ## Step 3: Build Open a **Developer Command Prompt for VS** (or a PowerShell session with the MSVC environment loaded), then run: ```bat theme={null} cd demo\cpp cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=Release cmake --build build ``` After the build, CMake automatically: * Copies `facebetter.dll` next to the executable in `build/` * Copies `resource/resource.fbd` to `build/resource/resource.fbd` * Copies `demo.png` (if present) to `build/resource/demo.png` ## Step 4: Prepare a Preview Image (optional) Place any face photo named `demo.png` in `demo/cpp/`: ``` demo/cpp/demo.png ``` The engine will process the image at \~30 fps and display the result in the left panel of the window. ## Step 5: Run ```bat theme={null} cd demo\cpp\build .\facebetter_demo.exe ``` Once running, the window shows: * **Left panel** – live beauty-processed preview * **Right panel** – Beauty Control Panel with the following groups: * **Basic Beauty**: Smoothing / Whitening / Rosiness / Sharpening * **Face Reshape**: Face Thin / V Face / Narrow Face / Short Face / Cheekbone / Jawbone / Chin / Nose Slim / Eye Size / Eye Distance * **Makeup**: Lipstick / Blush * **Sticker**: dropdown (Off / rabbit) Click **Reset All** to restore all parameters to zero. ## Troubleshooting **Q: "facebetter.dll not found" at startup**\ A: Make sure `demo/cpp/sdk/lib/facebetter.dll` exists, then re-run `cmake --build build` so CMake copies the DLL next to the executable. **Q: Window shows "Put demo.png …"**\ A: Place a face photo named `demo.png` in `demo/cpp/` and restart the demo. **Q: CMake cannot find Ninja**\ A: Run `winget install Ninja-build.Ninja`, or replace `-G "Ninja"` with `-G "Visual Studio 17 2022"` and change `--config Release` accordingly.