Android Camera 系统框架分析

转载地址:http://blog.csdn.net/xingyu19871124/article/details/7750189


一、在android中开发人员可以做那些工作?
        应用程序开发:利用android提供的强大的sdk,开发出各种各样新颖的应用。
        系统开发:在android中Google实现了与硬件无关的所有代码,但是与硬件密切相关的硬件抽象层却没有也无法提供,对于移动设备不同的设备提供商底层硬件是千变万化的,不可能提供统一的硬件驱动以及接口实现,只能提供标准的接口,因此硬件提供商需要自个儿开发设备驱动,
并去实现android框架提供的接口。
二、android框架中Camera系统源码分析
       在每个android手机中都有一个Camera应用程序用来实现拍照功能,不同硬件提供商可能会对这个应用程序进行改变来适合自己的UI风格,
这里仅仅分析android原生Camera应用以及框架(Android 4.0)
      原生Camera应用代码在Camera.java(android4.0\packages\apps\camera\src\com\android\camera),这个应该算是Camera系统最上层,应用层的实现。
      下面是Camera类部分代码

[java]  view plain copy
  1. public class Camera extends ActivityBase implements FocusManager.Listener,  
  2.         View.OnTouchListener, ShutterButton.OnShutterButtonListener,  
  3.         SurfaceHolder.Callback, ModePicker.OnModeChangeListener,  
  4.         FaceDetectionListener, CameraPreference.OnPreferenceChangedListener,  
  5.         LocationManager.Listener, ShutterButton.OnShutterButtonLongPressListener  
      从上面可以看出,Camera在继承了很多监听接口,用来监听各种事件(对焦事件、用户触摸事件等)。这个应用时继承ActivityBase,
可以重载OnCreate、OnResume等接口,在这些接口中完成相关初始化的工作,基本就是初始化各种监听对象,以及获取相机参数等相关。
     比较关键的在 doOnResume这个函数中:
[java]  view plain copy
  1. @Override  
  2. protected void doOnResume() {  
  3.     if (mOpenCameraFail || mCameraDisabled) return;  
  4.   
  5.     mPausing = false;  
  6.   
  7.     mJpegPictureCallbackTime = 0;  
  8.     mZoomValue = 0;  
  9.   
  10.     // Start the preview if it is not started.  
  11.     if (mCameraState == PREVIEW_STOPPED) {  
  12.         try {  
  13.             mCameraDevice = Util.openCamera(this, mCameraId);  
  14.             initializeCapabilities();  
  15.             resetExposureCompensation();  
  16.             startPreview();  
  17.             if (mFirstTimeInitialized) startFaceDetection();  
  18.         } catch (CameraHardwareException e) {  
  19.             Util.showErrorAndFinish(this, R.string.cannot_connect_camera);  
  20.             return;  
  21.         } catch (CameraDisabledException e) {  
  22.             Util.showErrorAndFinish(this, R.string.camera_disabled);  
  23.             return;  
  24.         }  
  25.     }  
  26.   
  27.     if (mSurfaceHolder != null) {  
  28.         // If first time initialization is not finished, put it in the  
  29.         // message queue.  
  30.         if (!mFirstTimeInitialized) {  
  31.             mHandler.sendEmptyMessage(FIRST_TIME_INIT);  
  32.         } else {  
  33.             initializeSecondTime();  
  34.         }  
  35.     }  
  36.     keepScreenOnAwhile();  
  37.   
  38.     if (mCameraState == IDLE) {  
  39.         mOnResumeTime = SystemClock.uptimeMillis();  
  40.         mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);  
  41.     }  
  42. }  
在这个函数中看到通过这个函数获得Camera底层对象
mCameraDevice = Util.openCamera(this, mCameraId),这里使用Util这个类,这个类的实现在
Util.java (android4.0\packages\apps\camera\src\com\android\camera)中,找到OpenCamera这个函数实现:
[java]  view plain copy
  1. public static android.hardware.Camera openCamera(Activity activity, int cameraId)  
  2.             throws CameraHardwareException, CameraDisabledException {  
  3.         // Check if device policy has disabled the camera.  
  4.         DevicePolicyManager dpm = (DevicePolicyManager) activity.getSystemService(  
  5.                 Context.DEVICE_POLICY_SERVICE);  
  6.         if (dpm.getCameraDisabled(null)) {  
  7.             throw new CameraDisabledException();  
  8.         }  
  9.   
  10.         try {  
  11.             return CameraHolder.instance().open(cameraId);  
  12.         } catch (CameraHardwareException e) {  
  13.             // In eng build, we throw the exception so that test tool  
  14.             // can detect it and report it  
  15.             if ("eng".equals(Build.TYPE)) {  
  16.                 throw new RuntimeException("openCamera failed", e);  
  17.             } else {  
  18.                 throw e;  
  19.             }  
  20.         }  
  21.     }  
从这个函数可以看出,android系统中对下层Camera管理,是通过一个单例模式CameraHolder来管理的,
定位到这个类的实现CameraHolder.java (android4.0\packages\apps\camera\src\com\android\camera)通过调用open函数获取一个Camera硬件设备对象,
因为Camera设备是独享设备,不能同时被两个进程占用,而整个android系统是一个多进程环境,因此需要加入一些进程间互斥同步的方法。
定位到这个类的open函数:
[java]  view plain copy
  1. public synchronized android.hardware.Camera open(int cameraId)  
  2.         throws CameraHardwareException {  
  3.     Assert(mUsers == 0);  
  4.     if (mCameraDevice != null && mCameraId != cameraId) {  
  5.         mCameraDevice.release();  
  6.         mCameraDevice = null;  
  7.         mCameraId = -1;  
  8.     }  
  9.     if (mCameraDevice == null) {  
  10.         try {  
  11.             Log.v(TAG, "open camera " + cameraId);  
  12.             mCameraDevice = android.hardware.Camera.open(cameraId);  
  13.             mCameraId = cameraId;  
  14.         } catch (RuntimeException e) {  
  15.             Log.e(TAG, "fail to connect Camera", e);  
  16.             throw new CameraHardwareException(e);  
  17.         }  
  18.         mParameters = mCameraDevice.getParameters();  
  19.     } else {  
  20.         try {  
  21.             mCameraDevice.reconnect();  
  22.         } catch (IOException e) {  
  23.             Log.e(TAG, "reconnect failed.");  
  24.             throw new CameraHardwareException(e);  
  25.         }  
  26.         mCameraDevice.setParameters(mParameters);  
  27.     }  
  28.     ++mUsers;  
  29.     mHandler.removeMessages(RELEASE_CAMERA);  
  30.     mKeepBeforeTime = 0;  
  31.     return mCameraDevice;  
  32. }  
[java]  view plain copy
  1.   
通过android.hardware.Camera.open(cameraId)调用进入下一层封装,JNI层,这一层是java代码的最下层,对下层CameraC++代码进行JNI封装,封装实现类在Camera.java (android4.0\frameworks\base\core\java\android\hardware) 下面是这个类的部分实现,里面定义了不少回调函数:
[java]  view plain copy
  1. public class Camera {  
  2.     private static final String TAG = "Camera";  
  3.   
  4.     // These match the enums in frameworks/base/include/camera/Camera.h  
  5.     private static final int CAMERA_MSG_ERROR            = 0x001;  
  6.     private static final int CAMERA_MSG_SHUTTER          = 0x002;  
  7.     private static final int CAMERA_MSG_FOCUS            = 0x004;  
  8.     private static final int CAMERA_MSG_ZOOM             = 0x008;  
  9.     private static final int CAMERA_MSG_PREVIEW_FRAME    = 0x010;  
  10.     private static final int CAMERA_MSG_VIDEO_FRAME      = 0x020;  
  11.     private static final int CAMERA_MSG_POSTVIEW_FRAME   = 0x040;  
  12.     private static final int CAMERA_MSG_RAW_IMAGE        = 0x080;  
  13.     private static final int CAMERA_MSG_COMPRESSED_IMAGE = 0x100;  
  14.     private static final int CAMERA_MSG_RAW_IMAGE_NOTIFY = 0x200;  
  15.     private static final int CAMERA_MSG_PREVIEW_METADATA = 0x400;  
  16.     private static final int CAMERA_MSG_ALL_MSGS         = 0x4FF;  
  17.   
  18.     private int mNativeContext; // accessed by native methods  
  19.     private EventHandler mEventHandler;  
  20.     private ShutterCallback mShutterCallback;  
  21.     private PictureCallback mRawImageCallback;  
  22.     private PictureCallback mJpegCallback;  
  23.     private PreviewCallback mPreviewCallback;  
  24.     private PictureCallback mPostviewCallback;  
  25.     private AutoFocusCallback mAutoFocusCallback;  
  26.     private OnZoomChangeListener mZoomListener;  
  27.     private FaceDetectionListener mFaceListener;  
  28.     private ErrorCallback mErrorCallback;  
定位到Open函数:
    public static Camera open(int cameraId) {
        return new Camera(cameraId);
    }
Open函数是一个静态方法,构造一个Camera对象:
[java]  view plain copy
  1. Camera(int cameraId) {  
  2.     mShutterCallback = null;  
  3.     mRawImageCallback = null;  
  4.     mJpegCallback = null;  
  5.     mPreviewCallback = null;  
  6.     mPostviewCallback = null;  
  7.     mZoomListener = null;  
  8.   
  9.     Looper looper;  
  10.     if ((looper = Looper.myLooper()) != null) {  
  11.         mEventHandler = new EventHandler(this, looper);  
  12.     } else if ((looper = Looper.getMainLooper()) != null) {  
  13.         mEventHandler = new EventHandler(this, looper);  
  14.     } else {  
  15.         mEventHandler = null;  
  16.     }  
  17.   
  18.     native_setup(new WeakReference<Camera>(this), cameraId);  
  19. }  

在构造函数中调用native_setup方法,此方法对应于C++代码的android_hardware_Camera_native_setup方法,
实现在android_hardware_Camera.cpp (android4.0\frameworks\base\core\jni),具体代码如下:
[cpp]  view plain copy
  1. static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,  
  2.     jobject weak_this, jint cameraId)  
  3. {  
  4.     sp<Camera> camera = Camera::connect(cameraId);  
  5.   
  6.     if (camera == NULL) {  
  7.         jniThrowRuntimeException(env, "Fail to connect to camera service");  
  8.         return;  
  9.     }  
  10.   
  11.     // make sure camera hardware is alive  
  12.     if (camera->getStatus() != NO_ERROR) {  
  13.         jniThrowRuntimeException(env, "Camera initialization failed");  
  14.         return;  
  15.     }  
  16.   
  17.     jclass clazz = env->GetObjectClass(thiz);  
  18.     if (clazz == NULL) {  
  19.         jniThrowRuntimeException(env, "Can't find android/hardware/Camera");  
  20.         return;  
  21.     }  
  22.   
  23.     // We use a weak reference so the Camera object can be garbage collected.  
  24.     // The reference is only used as a proxy for callbacks.  
  25.     sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);  
  26.     context->incStrong(thiz);  
  27.     camera->setListener(context);  
  28.   
  29.     // save context in opaque field  
  30.     env->SetIntField(thiz, fields.context, (int)context.get());  
  31. }  
在android_hardware_Camera_native_setup方法中调用了Camera对象的connect方法,这个Camera类的声明在Camera.h (android4.0\frameworks\base\include\camera)
定位到connect方法:
[cpp]  view plain copy
  1. sp<Camera> Camera::connect(int cameraId)  
  2. {  
  3.     LOGV("connect");  
  4.     sp<Camera> c = new Camera();  
  5.     const sp<ICameraService>& cs = getCameraService();  
  6.     if (cs != 0) {  
  7.         c->mCamera = cs->connect(c, cameraId);  
  8.     }  
  9.     if (c->mCamera != 0) {  
  10.         c->mCamera->asBinder()->linkToDeath(c);  
  11.         c->mStatus = NO_ERROR;  
  12.     } else {  
  13.         c.clear();  
  14.     }  
  15.     return c;  
  16. }  
这里以下的代码就比较关键了,涉及到Camera框架的实现机制,Camera系统使用的是Server-Client机制,Service和Client位于不同的进程中,进程间使用Binder机制进行通信,
Service端实际实现相机相关的操作,Client端通过Binder接口调用Service对应的操作。
继续分析代码,上面函数调用getCameraService方法,获得CameraService的引用,ICameraService有两个子类,BnCameraService和BpCameraService,这两个子类同时也
继承了IBinder接口,这两个子类分别实现了Binder通信的两端,Bnxxx实现ICameraService的具体功能,Bpxxx利用Binder的通信功能封装ICameraService方法,具体如下:
[cpp]  view plain copy
  1. class ICameraService : public IInterface  
  2. {  
  3. public:  
  4.     enum {  
  5.         GET_NUMBER_OF_CAMERAS = IBinder::FIRST_CALL_TRANSACTION,  
  6.         GET_CAMERA_INFO,  
  7.         CONNECT  
  8.     };  
  9.   
  10. public:  
  11.     DECLARE_META_INTERFACE(CameraService);  
  12.   
  13.     virtual int32_t         getNumberOfCameras() = 0;  
  14.     virtual status_t        getCameraInfo(int cameraId,  
  15.                                           struct CameraInfo* cameraInfo) = 0;  
  16.     virtual sp<ICamera>     connect(const sp<ICameraClient>& cameraClient,  
  17.                                     int cameraId) = 0;  
  18. };  
  19.   
  20. // ----------------------------------------------------------------------------  
  21.   
  22. class BnCameraService: public BnInterface<ICameraService>  
  23. {  
  24. public:  
  25.     virtual status_t    onTransact( uint32_t code,  
  26.                                     const Parcel& data,  
  27.                                     Parcel* reply,  
  28.                                     uint32_t flags = 0);  
  29. };  
  30.   
  31. }; // na  
[cpp]  view plain copy
  1. class BpCameraService: public BpInterface<ICameraService>  
  2. {  
  3. public:  
  4.     BpCameraService(const sp<IBinder>& impl)  
  5.         : BpInterface<ICameraService>(impl)  
  6.     {  
  7.     }  
  8.   
  9.     // get number of cameras available  
  10.     virtual int32_t getNumberOfCameras()  
  11.     {  
  12.         Parcel data, reply;  
  13.         data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());  
  14.         remote()->transact(BnCameraService::GET_NUMBER_OF_CAMERAS, data, &reply);  
  15.         return reply.readInt32();  
  16.     }  
  17.   
  18.     // get information about a camera  
  19.     virtual status_t getCameraInfo(int cameraId,  
  20.                                    struct CameraInfo* cameraInfo) {  
  21.         Parcel data, reply;  
  22.         data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());  
  23.         data.writeInt32(cameraId);  
  24.         remote()->transact(BnCameraService::GET_CAMERA_INFO, data, &reply);  
  25.         cameraInfo->facing = reply.readInt32();  
  26.         cameraInfo->orientation = reply.readInt32();  
  27.         return reply.readInt32();  
  28.     }  
  29.   
  30.     // connect to camera service  
  31.     virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId)  
  32.     {  
  33.         Parcel data, reply;  
  34.         data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());  
  35.         data.writeStrongBinder(cameraClient->asBinder());  
  36.         data.writeInt32(cameraId);  
  37.         remote()->transact(BnCameraService::CONNECT, data, &reply);  
  38.         return interface_cast<ICamera>(reply.readStrongBinder());  
  39.     }  
  40. };  
  41.   
  42. IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService");  
  43.   
  44. // ----------------------------------------------------------------------  
  45.   
  46. status_t BnCameraService::onTransact(  
  47.     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)  
  48. {  
  49.     switch(code) {  
  50.         case GET_NUMBER_OF_CAMERAS: {  
  51.             CHECK_INTERFACE(ICameraService, data, reply);  
  52.             reply->writeInt32(getNumberOfCameras());  
  53.             return NO_ERROR;  
  54.         } break;  
  55.         case GET_CAMERA_INFO: {  
  56.             CHECK_INTERFACE(ICameraService, data, reply);  
  57.             CameraInfo cameraInfo;  
  58.             memset(&cameraInfo, 0, sizeof(cameraInfo));  
  59.             status_t result = getCameraInfo(data.readInt32(), &cameraInfo);  
  60.             reply->writeInt32(cameraInfo.facing);  
  61.             reply->writeInt32(cameraInfo.orientation);  
  62.             reply->writeInt32(result);  
  63.             return NO_ERROR;  
  64.         } break;  
  65.         case CONNECT: {  
  66.             CHECK_INTERFACE(ICameraService, data, reply);  
  67.             sp<ICameraClient> cameraClient = interface_cast<ICameraClient>(data.readStrongBinder());  
  68.             sp<ICamera> camera = connect(cameraClient, data.readInt32());  
  69.             reply->writeStrongBinder(camera->asBinder());  
  70.             return NO_ERROR;  
  71.         } break;  
  72.         default:  
  73.             return BBinder::onTransact(code, data, reply, flags);  
  74.     }  
  75. }  
  76.   
  77. // ----------------------------------------------------------------------------  
  78.   
  79. }; // namespace android  
下面继续分析sp<Camera> Camera::connect(int cameraId)这个方法,,定位到getCameraService这个方法
[cpp]  view plain copy
  1. const sp<ICameraService>& Camera::getCameraService()  
  2. {  
  3.     Mutex::Autolock _l(mLock);  
  4.     if (mCameraService.get() == 0) {  
  5.         sp<IServiceManager> sm = defaultServiceManager();  
  6.         sp<IBinder> binder;  
  7.         do {  
  8.             binder = sm->getService(String16("media.camera"));  
  9.             if (binder != 0)  
  10.                 break;  
  11.             LOGW("CameraService not published, waiting...");  
  12.             usleep(500000); // 0.5 s  
  13.         } while(true);  
  14.         if (mDeathNotifier == NULL) {  
  15.             mDeathNotifier = new DeathNotifier();  
  16.         }  
  17.         binder->linkToDeath(mDeathNotifier);  
  18.         mCameraService = interface_cast<ICameraService>(binder);  
  19.     }  
  20.     LOGE_IF(mCameraService==0, "no CameraService!?");  
  21.     return mCameraService;  
  22. }  
定位到mCameraService = interface_cast<ICameraService>(binder); mCameraService是一个ICamerService类型,更加具体具体一点来讲应该是BpCameraService,
因为在这个类中实现了ICameraService的方法。

总结上面Binder机制,仅仅考虑分析Binder用法,对底层实现不进行深究,基本步骤如下:
1.定义进程间通信的接口比如这里的ICameraService;
2.在BnCameraService和BpCamaraService实现这个接口,这两个接口也分别继承于BnInterface和BpInterface;
3.服务端向ServiceManager注册Binder,客户端向ServiceManager获得Binder;
4.然后就可以实现双向进程间通信了;

通过getCameraService得到ICameraService引用后,调用ICameraService的connect方法获得ICamera引用,

[cpp]  view plain copy
  1. <pre name="code" class="cpp">c->mCamera = cs->connect(c, cameraId);</pre>进一步跟进connect方法,这里就是BpCameraService类中connect方法的具体实现。<br>  
  2. <pre name="code" class="cpp">    virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId)  
  3.     {  
  4.         Parcel data, reply;  
  5.         data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());  
  6.         data.writeStrongBinder(cameraClient->asBinder());  
  7.         data.writeInt32(cameraId);  
  8.         remote()->transact(BnCameraService::CONNECT, data, &reply);  
  9.         return interface_cast<ICamera>(reply.readStrongBinder());  
  10.     }</pre>在这里返回的ICamera对象,实际上应该是BpCamera对象,这里使用的是匿名Binder,前面获取CameraService的使用的有名Binder,有名Binder需要借助于ServiceManager获取Binder,而匿名Binder可以通过已经建立后的通信通道(有名Binder)获得。以上是实现Camera框架部分,具体的实现Camera相关的方法是在ICamera相关的接口,下面是给接口的定义:<pre name="code" class="cpp">class ICamera: public IInterface  
  11. {  
  12. public:  
  13.     DECLARE_META_INTERFACE(Camera);  
  14.   
  15.     virtual void            disconnect() = 0;  
  16.   
  17.     // connect new client with existing camera remote  
  18.     virtual status_t        connect(const sp<ICameraClient>& client) = 0;  
  19.   
  20.     // prevent other processes from using this ICamera interface  
  21.     virtual status_t        lock() = 0;  
  22.   
  23.     // allow other processes to use this ICamera interface  
  24.     virtual status_t        unlock() = 0;  
  25.   
  26.     // pass the buffered Surface to the camera service  
  27.     virtual status_t        setPreviewDisplay(const sp<Surface>& surface) = 0;  
  28.   
  29.     // pass the buffered ISurfaceTexture to the camera service  
  30.     virtual status_t        setPreviewTexture(  
  31.             const sp<ISurfaceTexture>& surfaceTexture) = 0;  
  32.   
  33.     // set the preview callback flag to affect how the received frames from  
  34.     // preview are handled.  
  35.     virtual void            setPreviewCallbackFlag(int flag) = 0;  
  36.   
  37.     // start preview mode, must call setPreviewDisplay first  
  38.     virtual status_t        startPreview() = 0;  
  39.   
  40.     // stop preview mode  
  41.     virtual void            stopPreview() = 0;  
  42.   
  43.     // get preview state  
  44.     virtual bool            previewEnabled() = 0;  
  45.   
  46.     // start recording mode  
  47.     virtual status_t        startRecording() = 0;  
  48.   
  49.     // stop recording mode  
  50.     virtual void            stopRecording() = 0;  
  51.   
  52.     // get recording state  
  53.     virtual bool            recordingEnabled() = 0;  
  54.   
  55.     // release a recording frame  
  56.     virtual void            releaseRecordingFrame(const sp<IMemory>& mem) = 0;  
  57.   
  58.     // auto focus  
  59.     virtual status_t        autoFocus() = 0;  
  60.   
  61.     // cancel auto focus  
  62.     virtual status_t        cancelAutoFocus() = 0;  
  63.   
  64.     /* 
  65.      * take a picture. 
  66.      * @param msgType the message type an application selectively turn on/off 
  67.      * on a photo-by-photo basis. The supported message types are: 
  68.      * CAMERA_MSG_SHUTTER, CAMERA_MSG_RAW_IMAGE, CAMERA_MSG_COMPRESSED_IMAGE, 
  69.      * and CAMERA_MSG_POSTVIEW_FRAME. Any other message types will be ignored. 
  70.      */  
  71.     virtual status_t        takePicture(int msgType) = 0;  
  72.   
  73.     // set preview/capture parameters - key/value pairs  
  74.     virtual status_t        setParameters(const String8& params) = 0;  
  75.   
  76.     // get preview/capture parameters - key/value pairs  
  77.     virtual String8         getParameters() const = 0;  
  78.   
  79.     // send command to camera driver  
  80.     virtual status_t        sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) = 0;  
  81.   
  82.     // tell the camera hal to store meta data or real YUV data in video buffers.  
  83.     virtual status_t        storeMetaDataInBuffers(bool enabled) = 0;  
  84. };</pre><br>  
  85. <pre name="code" class="cpp" style="background-color: rgb(255, 255, 255); ">ICamera接口有两个子类BnCamera和BpCamera,是Binder通信的两端,BpCamera提供客户端调用接口,BnCamera封装具体的实现,BnCamera也并没有真正实现ICamera相关接口而是在BnCamera子类CameraService::Client中进行实现。而在CameraService::Client类中会继续调用硬件抽象层中相关方法来具体实现Camera功能,  
  86.   
  87. 现在来缕一缕android中Camera各个类如何联系的  
  88. 。。。。未完</pre>  
  89. <pre></pre>  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值