android获取摄像头焦距,并调焦

本文展示了如何在Android应用中实现手动对焦功能,通过XML布局配置TextureView,创建CameraCaptureSession,设置CaptureRequest,并使用SeekBar调整镜头焦距。在用户拖动SeekBar时,更新CaptureRequest中的LENS_FOCUS_DISTANCE,实现实时焦点变化。代码涵盖了从初始化相机到捕获图像的完整流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

不废话,上代码

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    tools:context=".MainActivity">


    <TextureView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/textureView"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"/>



    <LinearLayout
        android:gravity="bottom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="拍照"
            android:gravity="center"
            android:id="@+id/photoButton"
            android:onClick="takePicture"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"/>


        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="-"
            android:id="@+id/sub"
            android:onClick="subfocus"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="+"
            android:id="@+id/add"
            android:onClick="addfocus"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"/>

        <SeekBar
            android:id="@+id/seekBar"
            android:layout_width="500dp"
            android:layout_height="match_parent"
            android:layout_marginTop="20dp"
            android:max="300"
            android:progress="100"/>
        <TextView
            android:id="@+id/tvProgress"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="数值范围0~100之间,当前值:30"
            android:textSize="20sp"
            android:gravity="center"/>

    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
package com.example.focus2;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.os.Build;
import android.os.Bundle;


        import android.Manifest;
        import android.app.Activity;
        import android.content.Context;
        import android.content.pm.PackageManager;
        import android.graphics.ImageFormat;
        import android.graphics.SurfaceTexture;
        import android.hardware.camera2.CameraAccessException;
        import android.hardware.camera2.CameraCaptureSession;
        import android.hardware.camera2.CameraCharacteristics;
        import android.hardware.camera2.CameraDevice;
        import android.hardware.camera2.CameraManager;
        import android.hardware.camera2.CameraMetadata;
        import android.hardware.camera2.CaptureRequest;
        import android.hardware.camera2.CaptureResult;
        import android.hardware.camera2.TotalCaptureResult;
        import android.hardware.camera2.params.StreamConfigurationMap;
        import android.media.Image;
        import android.media.ImageReader;
        import android.os.Bundle;
        import android.os.Environment;
        import android.os.Handler;
        import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
        import android.util.SparseIntArray;
        import android.view.Surface;
        import android.view.TextureView;
        import android.view.View;
        import android.view.Window;
        import android.view.WindowManager;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

        import java.io.File;
        import java.io.FileOutputStream;
        import java.io.IOException;
        import java.nio.ByteBuffer;
        import java.text.SimpleDateFormat;
        import java.util.ArrayList;
        import java.util.Arrays;
        import java.util.Collections;
        import java.util.Comparator;
        import java.util.Date;
        import java.util.List;

public class MainActivity extends Activity {
    private static final SparseIntArray ORIENTATION = new SparseIntArray();

    static {
        ORIENTATION.append(Surface.ROTATION_0, 90);
        ORIENTATION.append(Surface.ROTATION_90, 0);
        ORIENTATION.append(Surface.ROTATION_180, 270);
        ORIENTATION.append(Surface.ROTATION_270, 180);
    }

    static float m_len =0.0f;
    private String mCameraId;
    private Size mPreviewSize;
    private Size mCaptureSize;
    private HandlerThread mCameraThread;
    private Handler mCameraHandler;
    private CameraDevice mCameraDevice;
    private TextureView mTextureView;
    private ImageReader mImageReader;
    private CaptureRequest.Builder mCaptureRequestBuilder;
    private CaptureRequest mCaptureRequest;
    private CameraCaptureSession mCameraCaptureSession;

    private SeekBar seekBar;
    private TextView tvProgress;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //全屏无状态栏
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        setContentView(R.layout.activity_main);
        mTextureView = (TextureView) findViewById(R.id.textureView);


        seekBar = findViewById(R.id.seekBar);
        tvProgress = findViewById(R.id.tvProgress);
        //注册拖动监听器
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                //将seekBar的进度设置在textView上
                tvProgress.setText("当前值:"+progress);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                int m_pos = seekBar.getProgress();
                float f_pos= m_pos;
                f_pos = f_pos/10;


                mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
                mCaptureRequestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, f_pos);

                updatePreview(mCameraCaptureSession);

                String ls = CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION.toString();

                float tt = mCaptureRequestBuilder.get(CaptureRequest.LENS_FOCUS_DISTANCE);
                float tt1 = mCaptureRequestBuilder.get(CaptureRequest.LENS_FOCAL_LENGTH);


                Log.e("---CaptureRequest.LENS_FOCUS_DISTANCE = ", Float.toString(tt));

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                int m_pos = seekBar.getProgress();
                float f_pos= m_pos;
                f_pos = f_pos/10;


                mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
                mCaptureRequestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, f_pos);

                updatePreview(mCameraCaptureSession);

                float tt = mCaptureRequestBuilder.get(CaptureRequest.LENS_FOCUS_DISTANCE);


                Log.e("---CaptureRequest.LENS_FOCUS_DISTANCE = ", Float.toString(tt));

            }
        });

    }

    @Override
    protected void onResume() {
        super.onResume();
        startCameraThread();
        if (!mTextureView.isAvailable()) {
            mTextureView.setSurfaceTextureListener(mTextureListener);
        } else {
            startPreview();
        }
    }

    private void startCameraThread() {
        mCameraThread = new HandlerThread("CameraThread");
        mCameraThread.start();
        mCameraHandler = new Handler(mCameraThread.getLooper());
    }

    private TextureView.SurfaceTextureListener mTextureListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            //当SurefaceTexture可用的时候,设置相机参数并打开相机
            setupCamera(width, height);
            openCamera();
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            return false;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {

        }
    };

    @RequiresApi(api = Build.VERSION_CODES.P)
    private void setupCamera(int width, int height) {
        //获取摄像头的管理者CameraManager
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            //遍历所有摄像头
            for (String cameraId : manager.getCameraIdList()) {
                CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);

                CameraCharacteristics.Key<Integer> yourMinFocus = CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION;
//                float yourMaxFocus = CameraCharacteristics.get(CameraCharacteristics.LENS_INFO_HYPERFOCAL_disTANCE);
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                //此处默认打开后置摄像头
                if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT)
                    continue;
                //获取StreamConfigurationMap,它是管理摄像头支持的所有输出格式和尺寸
                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                assert map != null;
                //根据TextureView的尺寸设置预览尺寸
                mPreviewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height);
                //获取相机支持的最大拍照尺寸
                mCaptureSize = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), new Comparator<Size>() {
                    @Override
                    public int compare(Size lhs, Size rhs) {
                        return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getHeight() * rhs.getWidth());
                    }
                });
                //此ImageReader用于拍照所需
                setupImageReader();
                mCameraId = cameraId;
                break;
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    //选择sizeMap中大于并且最接近width和height的size
    private Size getOptimalSize(Size[] sizeMap, int width, int height) {
        List<Size> sizeList = new ArrayList<>();
        for (Size option : sizeMap) {
            if (width > height) {
                if (option.getWidth() > width && option.getHeight() > height) {
                    sizeList.add(option);
                }
            } else {
                if (option.getWidth() > height && option.getHeight() > width) {
                    sizeList.add(option);
                }
            }
        }
        if (sizeList.size() > 0) {
            return Collections.min(sizeList, new Comparator<Size>() {
                @Override
                public int compare(Size lhs, Size rhs) {
                    return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight());
                }
            });
        }
        return sizeMap[0];
    }


    private void openCamera() {
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            manager.openCamera(mCameraId, mStateCallback, mCameraHandler);
            CameraCharacteristics.Key<Integer> yourMinFocus = CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION;

             int mCameraId = CameraCharacteristics.LENS_FACING_FRONT; // 要打开的摄像头ID
             CameraCharacteristics mCameraCharacteristics; // 相机属性
            CameraManager mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

            mCameraCharacteristics = mCameraManager.getCameraCharacteristics(Integer.toString(mCameraId));
            StreamConfigurationMap map = mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            float yourMinFocus1[] = (float[]) mCameraCharacteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
            Log.d("---" , Float.toString(yourMinFocus1[0]));

            new Thread() {
                public void run() {
                    while (true) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                float []distances = new float[3];

                                float yourMinFocus1[] = (float[]) mCameraCharacteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
                                float yourMinFocus2[] = (float[]) mCameraCharacteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FILTER_DENSITIES);
                                int yourMinFocus3 =  mCameraCharacteristics.get(CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION);

//                                Log.d("1---" , Float.toString(yourMinFocus1[0]));
//                                Log.d("2---" , Float.toString(yourMinFocus2[0]));
//                                Log.d("3---" , Integer.toString(yourMinFocus3));


//                                m_len+=0.3f;
//                                mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
//                                mCaptureRequestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, m_len);
//                                Log.d("4---" , Float.toString(m_len));
//                                Log.d("5---" , (CaptureRequest.LENS_FOCUS_DISTANCE).toString());
//                                Log.d("6---" , CaptureRequest.LENS_FOCAL_LENGTH.toString());
//                                updatePreview(mCameraCaptureSession);
//
//                                float tt = mCaptureRequestBuilder.get(CaptureRequest.LENS_FOCUS_DISTANCE);
//
//
//                                Log.e("7---", Float.toString(tt));

//                                startPreview();

//                                mCaptureRequestBuilder.get(CaptureRequest.LENS_FOCUS_DISTANCE, m_len);



                            }
                        });
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();


            Log.d("------" ,yourMinFocus.toString() );
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void updatePreview(CameraCaptureSession session) {
        try {
            session.setRepeatingRequest(mCaptureRequestBuilder.build(), null, mCameraHandler);

        } catch (CameraAccessException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice camera) {
            mCameraDevice = camera;
            startPreview();
        }

        @Override
        public void onDisconnected(CameraDevice camera) {
            camera.close();
            mCameraDevice = null;
        }

        @Override
        public void onError(CameraDevice camera, int error) {
            camera.close();
            mCameraDevice = null;
        }
    };

    private void startPreview() {
        Log.d("-----------" , "startPreview");
        SurfaceTexture mSurfaceTexture = mTextureView.getSurfaceTexture();
        mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
        Surface previewSurface = new Surface(mSurfaceTexture);
        try {
            mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mCaptureRequestBuilder.addTarget(previewSurface);
            mCameraDevice.createCaptureSession(Arrays.asList(previewSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(CameraCaptureSession session) {
                    try {
                        mCaptureRequest = mCaptureRequestBuilder.build();
                        mCameraCaptureSession = session;
                        mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, null, mCameraHandler);
                        Log.d("-----------" , "startPreview");
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onConfigureFailed(CameraCaptureSession session) {

                }
            }, mCameraHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    public void takePicture(View view) {
        lockFocus();
        Log.d("-----------" , "take");
    }

    public void subfocus(View view)
    {
        m_len=mCaptureRequestBuilder.get(CaptureRequest.LENS_FOCUS_DISTANCE);
        m_len-=0.3f;

        mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
        mCaptureRequestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, m_len);

        updatePreview(mCameraCaptureSession);

        float tt = mCaptureRequestBuilder.get(CaptureRequest.LENS_FOCUS_DISTANCE);


        Log.e("---CaptureRequest.LENS_FOCUS_DISTANCE = ", Float.toString(tt));

    }

    public void addfocus(View view)
    {
        m_len =mCaptureRequestBuilder.get(CaptureRequest.LENS_FOCUS_DISTANCE);
        m_len+=0.3f;

        mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
        mCaptureRequestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, m_len);

        updatePreview(mCameraCaptureSession);

        float tt = mCaptureRequestBuilder.get(CaptureRequest.LENS_FOCUS_DISTANCE);


        Log.e("---CaptureRequest.LENS_FOCUS_DISTANCE = ", Float.toString(tt));
    }

    private void lockFocus() {
        try {
            mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
            mCameraCaptureSession.capture(mCaptureRequestBuilder.build(), mCaptureCallback, mCameraHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
        @Override
        public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) {
        }

        @Override
        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
            capture();
        }
    };

    private void capture() {
        try {
            final CaptureRequest.Builder mCaptureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            int rotation = getWindowManager().getDefaultDisplay().getRotation();
            mCaptureBuilder.addTarget(mImageReader.getSurface());
            mCaptureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATION.get(rotation));
            CameraCaptureSession.CaptureCallback CaptureCallback = new CameraCaptureSession.CaptureCallback() {
                @Override
                public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                    Toast.makeText(getApplicationContext(), "Image Saved!", Toast.LENGTH_SHORT).show();
                    unLockFocus();
                }
            };
            mCameraCaptureSession.stopRepeating();
            mCameraCaptureSession.capture(mCaptureBuilder.build(), CaptureCallback, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void unLockFocus() {
        try {
            mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
            //mCameraCaptureSession.capture(mCaptureRequestBuilder.build(), null, mCameraHandler);
            mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, null, mCameraHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mCameraCaptureSession != null) {
            mCameraCaptureSession.close();
            mCameraCaptureSession = null;
        }

        if (mCameraDevice != null) {
            mCameraDevice.close();
            mCameraDevice = null;
        }

        if (mImageReader != null) {
            mImageReader.close();
            mImageReader = null;
        }
    }

    private void setupImageReader() {
        //2代表ImageReader中最多可以获取两帧图像流
        mImageReader = ImageReader.newInstance(mCaptureSize.getWidth(), mCaptureSize.getHeight(),
                ImageFormat.JPEG, 2);
        mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                mCameraHandler.post(new imageSaver(reader.acquireNextImage()));
            }
        }, mCameraHandler);
    }

    public static class imageSaver implements Runnable {

        private Image mImage;

        public imageSaver(Image image) {
            mImage = image;
        }

        @Override
        public void run() {
            ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
            byte[] data = new byte[buffer.remaining()];
            buffer.get(data);
            String path = Environment.getExternalStorageDirectory() + "/DCIM/CameraV2/";
            File mImageFile = new File(path);
            if (!mImageFile.exists()) {
                mImageFile.mkdir();
            }
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
            String fileName = path + "IMG_" + timeStamp + ".jpg";
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(fileName);
                fos.write(data, 0, data.length);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

在OpenCV中,获取摄像头焦距可以通过测量目标物体与摄像头之间的距离以及图像上目标的像素宽度来间接实现。这个过程通常涉及以下几个步骤[^1]: 1. **测量目标物宽度**: 首先,你需要找到图像上的某个已知尺寸的目标(如一个标记),其实际宽度是已知的。 2. **距离计算**: 使用`distance_to_camera`函数,该函数接受三个参数:已知目标宽度(knownWidth)、假设的焦距(focalLength)(这通常是根据相机规格预先设定的),以及目标在图像上的像素宽度(perWidth)。这个函数基于相似三角形原理计算目标到相机的距离。 3. **获取焦距**: 如果你知道目标的实际宽度和它在图像上的像素宽度,你可以调用`distance_to_camera`函数来反推出焦距。然而,注意这不是真正的摄像机标定,摄像机标定涉及到更复杂的内参信息。 具体操作示例代码可能如下所示(Python接口可能有所不同,但概念不变): ```python # 假设你已经有了目标宽度(knownWidth)和图像上的像素宽度(perWidth) knownWidth = ... # 实际宽度 perWidth = ... # 目标在图像上的像素宽度 # 假设你已经从文档或相机规格中得到了focalLength focalLength = ... # 计算距离 camera_distance = distance_to_camera(knownWidth, focalLength, perWidth) # 注意:上述步骤仅适用于特定情况,完整的焦距计算通常依赖于更详细的摄像机参数设置 ```
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值