{"id":1639,"date":"2023-03-25T21:39:29","date_gmt":"2023-03-25T13:39:29","guid":{"rendered":"https:\/\/www.appblog.cn\/?p=1639"},"modified":"2023-04-23T22:00:17","modified_gmt":"2023-04-23T14:00:17","slug":"android-jetpack-architecture-components-basic-features-of-camerax","status":"publish","type":"post","link":"https:\/\/www.appblog.cn\/index.php\/2023\/03\/25\/android-jetpack-architecture-components-basic-features-of-camerax\/","title":{"rendered":"Android Jetpack\u67b6\u6784\u7ec4\u4ef6\uff08\u56db\uff09CameraX\u57fa\u672c\u529f\u80fd"},"content":{"rendered":"<p>\u672c\u6587\u4f7f\u7528\u7684CameraX\u7248\u672c\u4e3a<code>1.0.0-beta03<\/code>\uff0c\u5305\u542bCameraX\u7684\u7b80\u5355\u62cd\u7167\u4fdd\u5b58\u3001\u56fe\u50cf\u5206\u6790\uff08\u53ef\u7528\u4e8e\u4e8c\u7ef4\u7801\u8bc6\u522b\u7b49\u7528\u9014\uff09\u3001\u7f29\u653e\u3001\u5bf9\u7126\u7b49\u76f8\u5173\u5185\u5bb9<\/p>\n<h2>\u57fa\u7840\u4f7f\u7528<\/h2>\n<h3>xml\u5e03\u5c40<\/h3>\n<p><!-- more --><\/p>\n<pre><code class=\"language-xml\">&lt;androidx.camera.view.PreviewView\n    android:id=&quot;@+id\/view_finder&quot;\n    android:layout_width=&quot;0dp&quot;\n    android:layout_height=&quot;0dp&quot;\n    app:layout_constraintBottom_toBottomOf=&quot;parent&quot;\n    app:layout_constraintEnd_toEndOf=&quot;parent&quot;\n    app:layout_constraintStart_toStartOf=&quot;parent&quot;\n    app:layout_constraintTop_toTopOf=&quot;parent&quot;\n    \/&gt;<\/code><\/pre>\n<h3>\u6743\u9650\u58f0\u660e<\/h3>\n<pre><code class=\"language-xml\">&lt;uses-permission android:name=&quot;android.permission.WRITE_EXTERNAL_STORAGE&quot; \/&gt;\n&lt;uses-permission android:name=&quot;android.permission.READ_EXTERNAL_STORAGE&quot; \/&gt;\n&lt;uses-permission android:name=&quot;android.permission.FLASHLIGHT&quot; \/&gt;\n&lt;uses-permission android:name=&quot;android.permission.CAMERA&quot; \/&gt;\n&lt;uses-feature android:name=&quot;android.hardware.camera&quot; \/&gt;\n&lt;uses-feature android:name=&quot;android.hardware.camera.autofocus&quot; \/&gt;<\/code><\/pre>\n<p>\u52a8\u6001\u6743\u9650\u4ee3\u7801\u7565<\/p>\n<h3>\u6784\u5efa\u56fe\u50cf\u6355\u83b7\u7528\u4f8b<\/h3>\n<pre><code class=\"language-java\">private void initImageCapture() {\n\n    \/\/ \u6784\u5efa\u56fe\u50cf\u6355\u83b7\u7528\u4f8b\n    mImageCapture = new ImageCapture.Builder()\n            .setFlashMode(ImageCapture.FLASH_MODE_AUTO)\n            .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n            .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)\n            .build();\n\n    \/\/ \u65cb\u8f6c\u76d1\u542c\n    OrientationEventListener orientationEventListener = new OrientationEventListener((Context) this) {\n        @Override\n        public void onOrientationChanged(int orientation) {\n            int rotation;\n\n            \/\/ Monitors orientation values to determine the target rotation value\n            if (orientation &gt;= 45 &amp;&amp; orientation &lt; 135) {\n                rotation = Surface.ROTATION_270;\n            } else if (orientation &gt;= 135 &amp;&amp; orientation &lt; 225) {\n                rotation = Surface.ROTATION_180;\n            } else if (orientation &gt;= 225 &amp;&amp; orientation &lt; 315) {\n                rotation = Surface.ROTATION_90;\n            } else {\n                rotation = Surface.ROTATION_0;\n            }\n\n            mImageCapture.setTargetRotation(rotation);\n        }\n    };\n\n    orientationEventListener.enable();\n}<\/code><\/pre>\n<h3>\u6784\u5efa\u56fe\u50cf\u5206\u6790\u7528\u4f8b(\u53ef\u7528\u4e8e\u4e8c\u7ef4\u7801\u8bc6\u522b\u7b49\u7528\u9014)<\/h3>\n<p>\u6ce8\u610f\uff1a<code>Analyzer<\/code>\u56de\u8c03\u65b9\u6cd5\u4e2d\u5982\u679c\u4e0d\u8c03\u7528<code>image.close()<\/code>\u5c06\u4e0d\u4f1a\u83b7\u53d6\u5230\u4e0b\u4e00\u5f20\u56fe\u7247<\/p>\n<pre><code class=\"language-java\">private void initImageAnalysis() {\n\n    mImageAnalysis = new ImageAnalysis.Builder()\n            \/\/ \u5206\u8fa8\u7387\n            .setTargetResolution(new Size(1280, 720))\n            \/\/ \u4ec5\u5c06\u6700\u65b0\u56fe\u50cf\u4f20\u9001\u5230\u5206\u6790\u4eea\uff0c\u5e76\u5728\u5230\u8fbe\u56fe\u50cf\u65f6\u5c06\u5176\u4e22\u5f03\u3002\n            .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)\n            .build();\n\n    mImageAnalysis.setAnalyzer(executor, image -&gt; {\n        int rotationDegrees = image.getImageInfo().getRotationDegrees();\n        Log.e(&quot;yezhou&quot;, &quot;Analysis#rotationDegrees: &quot; + rotationDegrees);\n        ImageProxy.PlaneProxy[] planes = image.getPlanes();\n\n        ByteBuffer buffer = planes[0].getBuffer();\n        \/\/ \u8f6c\u4e3abyte[]\n        \/\/ byte[] b = new byte[buffer.remaining()];\n        \/\/ Log.e(&quot;yezhou&quot;, b);\n        \/\/ TODO: \u5206\u6790\u5b8c\u6210\u540e\u5173\u95ed\u56fe\u50cf\u53c2\u8003\uff0c\u5426\u5219\u4f1a\u963b\u585e\u5176\u4ed6\u56fe\u50cf\u7684\u4ea7\u751f\n        \/\/ image.close();\n    });\n}<\/code><\/pre>\n<h3>\u521d\u59cb\u5316\u76f8\u673a<\/h3>\n<pre><code class=\"language-java\">private Executor executor;\n...\nprivate void initCamera() {\n\n    executor = ContextCompat.getMainExecutor(this);\n\n    cameraProviderFuture = ProcessCameraProvider.getInstance(this);\n    cameraProviderFuture.addListener(() -&gt; {\n        try {\n            ProcessCameraProvider cameraProvider = cameraProviderFuture.get();\n            \/\/ \u7ed1\u5b9a\u9884\u89c8\n            bindPreview(cameraProvider);\n        } catch (ExecutionException | InterruptedException e) {\n            \/\/ No errors need to be handled for this Future.\n            \/\/ This should never be reached.\n        }\n    }, executor);\n\n}<\/code><\/pre>\n<h3>\u7ed1\u5b9a\u9884\u89c8<\/h3>\n<pre><code class=\"language-java\">private void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {\n    Preview preview = new Preview.Builder()\n            .build();\n\n    CameraSelector cameraSelector = new CameraSelector.Builder()\n            .requireLensFacing(CameraSelector.LENS_FACING_BACK)\n            .build();\n\n    \/\/ mImageCapture \u56fe\u50cf\u6355\u83b7\u7528\u4f8b\n    \/\/ mImageAnalysis \u56fe\u50cf\u5206\u6790\u7528\u4f8b\n    Camera camera = cameraProvider.bindToLifecycle(this, cameraSelector, mImageCapture, mImageAnalysis, preview);\n\n    mCameraInfo = camera.getCameraInfo();\n    mCameraControl = camera.getCameraControl();\n\n    preview.setSurfaceProvider(mViewFinder.createSurfaceProvider(mCameraInfo));\n\n    initCameraListener();\n}<\/code><\/pre>\n<h3>\u62cd\u7167\u4fdd\u5b58\u56fe\u7247<\/h3>\n<pre><code class=\"language-java\">public void saveImage() {\n    File file = new File(getExternalMediaDirs()[0], System.currentTimeMillis() + &quot;.jpg&quot;);\n    ImageCapture.OutputFileOptions outputFileOptions =\n            new ImageCapture.OutputFileOptions.Builder(file).build();\n    mImageCapture.takePicture(outputFileOptions, executor,\n            new ImageCapture.OnImageSavedCallback() {\n\n                @Override\n                public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {\n                    String msg = &quot;\u56fe\u7247\u4fdd\u5b58\u6210\u529f: &quot; + file.getAbsolutePath();\n                    showMsg(msg);\n                    Log.i(&quot;yezhou&quot;, msg);\n                    Uri contentUri = Uri.fromFile(new File(file.getAbsolutePath()));\n                    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, contentUri);\n                    sendBroadcast(mediaScanIntent);\n                }\n\n                @Override\n                public void onError(@NonNull ImageCaptureException exception) {\n                    String msg = &quot;\u56fe\u7247\u4fdd\u5b58\u5931\u8d25: &quot; + exception.getMessage();\n                    showMsg(msg);\n                    Log.e(&quot;yezhou&quot;, msg);\n                }\n            }\n    );\n}<\/code><\/pre>\n<h2>\u81ea\u5b9a\u4e49PreviewView\u5b9e\u73b0\u624b\u52bf\u4e8b\u4ef6<\/h2>\n<p>\u70b9\u51fb\u3001\u53cc\u51fb\u3001\u7f29\u653e\u3001\u957f\u6309\u7b49<\/p>\n<h3>\u56de\u8c03\u63a5\u53e3<\/h3>\n<pre><code class=\"language-java\">public interface CustomTouchListener {\n    \/**\n     * \u653e\u5927\n     *\/\n    void zoom();\n\n    \/**\n     * \u7f29\u5c0f\n     *\/\n    void ZoomOut();\n\n    \/**\n     * \u70b9\u51fb\n     *\/\n    void click(float x, float y);\n\n    \/**\n     * \u53cc\u51fb\n     *\/\n    void doubleClick(float x, float y);\n\n    \/**\n     * \u957f\u6309\n     *\/\n    void longClick(float x, float y);\n}<\/code><\/pre>\n<h3>\u624b\u52bf\u76d1\u542c\u4ee3\u7801<\/h3>\n<pre><code class=\"language-java\">public class CameraXCustomPreviewView extends PreviewView {\n    private GestureDetector mGestureDetector;\n    \/**\n    * \u7f29\u653e\u76f8\u5173\n    *\/\n    private float currentDistance = 0;\n    private float lastDistance = 0;\n\n    ... \u7701\u7565\u90e8\u5206\u6784\u9020\u65b9\u6cd5\n\n    public CameraXCustomPreviewView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {\n        super(context, attrs, defStyleAttr, defStyleRes);\n\n        mGestureDetector = new GestureDetector(context, onGestureListener);\n        mGestureDetector.setOnDoubleTapListener(onDoubleTapListener);\n\n        \/\/ mScaleGestureDetector = new ScaleGestureDetector(context, onScaleGestureListener);\n        \/\/ \u89e3\u51b3\u957f\u6309\u5c4f\u5e55\u65e0\u6cd5\u62d6\u52a8,\u4f46\u662f\u4f1a\u9020\u6210\u65e0\u6cd5\u8bc6\u522b\u957f\u6309\u4e8b\u4ef6\n        \/\/ mGestureDetector.setIsLongpressEnabled(false);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        \/\/ \u63a5\u7ba1onTouchEvent\n        return mGestureDetector.onTouchEvent(event);\n    }\n\n    GestureDetector.OnGestureListener onGestureListener = new GestureDetector.OnGestureListener() {\n        @Override\n        public boolean onDown(MotionEvent e) {\n            LogUtils.i(&quot;onDown: \u6309\u4e0b&quot;);\n            return true;\n        }\n\n        @Override\n        public void onShowPress(MotionEvent e) {\n            LogUtils.i(&quot;onShowPress: \u521a\u78b0\u4e0a\u8fd8\u6ca1\u677e\u5f00&quot;);\n        }\n\n        @Override\n        public boolean onSingleTapUp(MotionEvent e) {\n            LogUtils.i(&quot;onSingleTapUp: \u8f7b\u8f7b\u4e00\u78b0\u540e\u9a6c\u4e0a\u677e\u5f00&quot;);\n            return true;\n        }\n\n        @Override\n        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {\n            LogUtils.i(&quot;onScroll: \u6309\u4e0b\u540e\u62d6\u52a8&quot;);\n            \/\/ \u5927\u4e8e\u4e24\u4e2a\u89e6\u6478\u70b9\n            if (e2.getPointerCount() &gt;= 2) {\n\n                \/\/event\u4e2d\u5c01\u5b58\u4e86\u6240\u6709\u5c4f\u5e55\u88ab\u89e6\u6478\u7684\u70b9\u7684\u4fe1\u606f\uff0c\u7b2c\u4e00\u4e2a\u89e6\u6478\u7684\u4f4d\u7f6e\u53ef\u4ee5\u901a\u8fc7event.getX(0)\/getY(0)\u5f97\u5230\n                float offSetX = e2.getX(0) - e2.getX(1);\n                float offSetY = e2.getY(0) - e2.getY(1);\n                \/\/\u8fd0\u7528\u4e09\u89d2\u51fd\u6570\u7684\u516c\u5f0f\uff0c\u901a\u8fc7\u8ba1\u7b97X,Y\u5750\u6807\u7684\u5dee\u503c\uff0c\u8ba1\u7b97\u4e24\u70b9\u95f4\u7684\u8ddd\u79bb\n                currentDistance = (float) Math.sqrt(offSetX * offSetX + offSetY * offSetY);\n                if (lastDistance == 0) {\/\/\u5982\u679c\u662f\u7b2c\u4e00\u6b21\u8fdb\u884c\u5224\u65ad\n                    lastDistance = currentDistance;\n                } else {\n                    if (currentDistance - lastDistance &gt; 10) {\n                        \/\/ \u653e\u5927\n                        if (mCustomTouchListener != null) {\n                            mCustomTouchListener.zoom();\n                        }\n                    } else if (lastDistance - currentDistance &gt; 10) {\n                        \/\/ \u7f29\u5c0f\n                        if (mCustomTouchListener != null) {\n                            mCustomTouchListener.ZoomOut();\n                        }\n                    }\n                }\n                \/\/\u5728\u4e00\u6b21\u7f29\u653e\u64cd\u4f5c\u5b8c\u6210\u540e\uff0c\u5c06\u672c\u6b21\u7684\u8ddd\u79bb\u8d4b\u503c\u7ed9lastDistance\uff0c\u4ee5\u4fbf\u4e0b\u4e00\u6b21\u5224\u65ad\n                \/\/\u4f46\u8fd9\u79cd\u65b9\u6cd5\u5199\u5728move\u52a8\u4f5c\u4e2d\uff0c\u610f\u5473\u7740\u624b\u6307\u4e00\u76f4\u6ca1\u6709\u62ac\u8d77\uff0c\u76d1\u63a7\u4e24\u624b\u6307\u4e4b\u95f4\u7684\u53d8\u5316\u8ddd\u79bb\u8d85\u8fc710\n                \/\/\u5c31\u6267\u884c\u7f29\u653e\u64cd\u4f5c\uff0c\u4e0d\u662f\u5728\u4e24\u6b21\u70b9\u51fb\u4e4b\u95f4\u7684\u8ddd\u79bb\u53d8\u5316\u6765\u5224\u65ad\u7f29\u653e\u64cd\u4f5c\n                \/\/\u6545\u8fd9\u79cd\u5c06\u672c\u6b21\u8ddd\u79bb\u7559\u5f85\u4e0b\u4e00\u6b21\u5224\u65ad\u7684\u65b9\u6cd5\uff0c\u4e0d\u80fd\u5728\u4e24\u6b21\u70b9\u51fb\u4e4b\u95f4\u4f7f\u7528\n                lastDistance = currentDistance;\n            }\n            return true;\n        }\n\n        @Override\n        public void onLongPress(MotionEvent e) {\n            LogUtils.i(&quot;onLongPress: \u957f\u6309\u5c4f\u5e55&quot;);\n            if (mCustomTouchListener != null) {\n                mCustomTouchListener.longClick(e.getX(), e.getY());\n            }\n        }\n\n        @Override\n        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {\n            LogUtils.i(&quot;onFling: \u6ed1\u52a8\u540e\u677e\u5f00&quot;);\n            currentDistance = 0;\n            lastDistance = 0;\n            return true;\n        }\n    };\n\n    GestureDetector.OnDoubleTapListener onDoubleTapListener = new GestureDetector.OnDoubleTapListener() {\n        @Override\n        public boolean onSingleTapConfirmed(MotionEvent e) {\n            LogUtils.i(&quot;onSingleTapConfirmed: \u4e25\u683c\u7684\u5355\u51fb&quot;);\n            if (mCustomTouchListener != null) {\n                mCustomTouchListener.click(e.getX(), e.getY());\n            }\n            return true;\n        }\n\n        @Override\n        public boolean onDoubleTap(MotionEvent e) {\n            LogUtils.i(&quot;onDoubleTap: \u53cc\u51fb&quot;);\n            if (mCustomTouchListener != null) {\n                mCustomTouchListener.doubleClick(e.getX(), e.getY());\n            }\n            return true;\n        }\n\n        @Override\n        public boolean onDoubleTapEvent(MotionEvent e) {\n            LogUtils.i(&quot;onDoubleTapEvent: \u8868\u793a\u53d1\u751f\u53cc\u51fb\u884c\u4e3a&quot;);\n            return true;\n        }\n    };\n}<\/code><\/pre>\n<h2>\u53cc\u6307\u6ed1\u52a8\/\u53cc\u51fb\u7f29\u653e\u3001\u70b9\u51fb\u5bf9\u7126<\/h2>\n<pre><code class=\"language-java\">private void initCameraListener() {\n    LiveData&lt;ZoomState&gt; zoomState = mCameraInfo.getZoomState();\n    float maxZoomRatio = zoomState.getValue().getMaxZoomRatio();\n    float minZoomRatio = zoomState.getValue().getMinZoomRatio();\n    LogUtils.e(maxZoomRatio);\n    LogUtils.e(minZoomRatio);\n\n    mViewFinder.setCustomTouchListener(new CameraXCustomPreviewView.CustomTouchListener() {\n        @Override\n        public void zoom() {\n            float zoomRatio = zoomState.getValue().getZoomRatio();\n            if (zoomRatio &lt; maxZoomRatio) {\n                mCameraControl.setZoomRatio((float) (zoomRatio + 0.1));\n            }\n        }\n\n        @Override\n        public void ZoomOut() {\n            float zoomRatio = zoomState.getValue().getZoomRatio();\n            if (zoomRatio &gt; minZoomRatio) {\n                mCameraControl.setZoomRatio((float) (zoomRatio - 0.1));\n            }\n        }\n\n        @Override\n        public void click(float x, float y) {\n            \/\/ TODO \u5bf9\u7126\n            MeteringPointFactory factory = new SurfaceOrientedMeteringPointFactory(1.0f, 1.0f);\n            MeteringPoint point = factory.createPoint(x, y);\n            FocusMeteringAction action = new FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF)\n                    \/\/ auto calling cancelFocusAndMetering in 3 seconds\n                    .setAutoCancelDuration(3, TimeUnit.SECONDS)\n                    .build();\n\n            mFocusView.startFocus(new Point((int) x, (int) y));\n            ListenableFuture future = mCameraControl.startFocusAndMetering(action);\n            future.addListener(() -&gt; {\n                try {\n                    FocusMeteringResult result = (FocusMeteringResult) future.get();\n                    if (result.isFocusSuccessful()) {\n                        mFocusView.onFocusSuccess();\n                    } else {\n                        mFocusView.onFocusFailed();\n                    }\n                } catch (Exception e) {\n                }\n            }, executor);\n        }\n\n        @Override\n        public void doubleClick(float x, float y) {\n            \/\/ \u53cc\u51fb\u653e\u5927\u7f29\u5c0f\n            float zoomRatio = zoomState.getValue().getZoomRatio();\n            if (zoomRatio &gt; minZoomRatio) {\n                mCameraControl.setLinearZoom(0f);\n            } else {\n                mCameraControl.setLinearZoom(0.5f);\n            }\n        }\n\n        @Override\n        public void longClick(float x, float y) {\n\n        }\n    });\n}<\/code><\/pre>\n<h2>\u95ea\u5149\u706f<\/h2>\n<pre><code class=\"language-java\">switch (mImageCapture.getFlashMode()) {\n    case ImageCapture.FLASH_MODE_AUTO:\n        mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_ON);\n        mBtnLight.setText(&quot;\u95ea\u5149\u706f\uff1a\u5f00&quot;);\n        break;\n    case ImageCapture.FLASH_MODE_ON:\n        mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_OFF);\n        mBtnLight.setText(&quot;\u95ea\u5149\u706f\uff1a\u5173&quot;);\n        break;\n    case ImageCapture.FLASH_MODE_OFF:\n        mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_AUTO);\n        mBtnLight.setText(&quot;\u95ea\u5149\u706f\uff1a\u81ea\u52a8&quot;);\n        break;\n}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\u672c\u6587\u4f7f\u7528\u7684CameraX\u7248\u672c\u4e3a1.0.0-beta03\uff0c\u5305\u542bCameraX\u7684\u7b80\u5355\u62cd\u7167\u4fdd\u5b58\u3001\u56fe\u50cf\u5206\u6790\uff08\u53ef\u7528\u4e8e\u4e8c\u7ef4 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[311],"tags":[411,410],"class_list":["post-1639","post","type-post","status-publish","format-standard","hentry","category-android-advance","tag-camerax","tag-jetpack"],"_links":{"self":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/1639","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/comments?post=1639"}],"version-history":[{"count":0,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/1639\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/media?parent=1639"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/categories?post=1639"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/tags?post=1639"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}