Android多媒体框架(3)—— libstagefright中MediaCodec源码分析

StageFright框架流程解读 <br />1、    StageFright介绍<br />     Android froyo版本多媒体引擎做了变动,新添加了stagefright框架,并且默认情况android选择stagefright,并没有完全抛弃opencore,主要是做了一个OMX层,仅仅是对 opencore的omx-component部分做了引用。stagefright是在MediaPlayerService这一层加入的,和opencore是并列的。Stagefright在 Android中是以shared libra 阅读详情

libstagefright中MediaCodec源码分析

和前两篇一样,我们按照MediaCodec的各个状态来分析libstagefright中MediaCodec的源代码。

  • configure
    首先我们看一下configure在libstagefright中MediaCodec中的定义:

438status_t MediaCodec::configure(
439        const sp<AMessage> &format,
440        const sp<Surface> &surface,
441        const sp<ICrypto> &crypto,
442        uint32_t flags) {
443    sp<AMessage> msg = new AMessage(kWhatConfigure, this);
444
445    if (mIsVideo) {
446        format->findInt32("width", &mVideoWidth);
447        format->findInt32("height", &mVideoHeight);
448        if (!format->findInt32("rotation-degrees", &mRotationDegrees)) {
449            mRotationDegrees = 0;
450        }
451
452        // Prevent possible integer overflow in downstream code.
453        if (mInitIsEncoder
454                && (uint64_t)mVideoWidth * mVideoHeight > (uint64_t)INT32_MAX / 4) {
455            ALOGE("buffer size is too big, width=%d, height=%d", mVideoWidth, mVideoHeight);
456            return BAD_VALUE;
457        }
458    }
459
460    msg->setMessage("format", format);
461    msg->setInt32("flags", flags);
462    msg->setObject("surface", surface);
463
464    if (crypto != NULL) {
465        msg->setPointer("crypto", crypto.get());
466    }
467
468    // save msg for reset
469    mConfigureMsg = msg;
470
471    status_t err;
472    Vector<MediaResource> resources;
473    MediaResource::Type type = (mFlags & kFlagIsSecure) ?
474            MediaResource::kSecureCodec : MediaResource::kNonSecureCodec;
475    MediaResource::SubType subtype =
476            mIsVideo ? MediaResource::kVideoCodec : MediaResource::kAudioCodec;
477    resources.push_back(MediaResource(type, subtype, 1));
478    // Don't know the buffer size at this point, but it's fine to use 1 because
479    // the reclaimResource call doesn't consider the requester's buffer size for now.
480    resources.push_back(MediaResource(MediaResource::kGraphicMemory, 1));
481    for (int i = 0; i <= kMaxRetry; ++i) {
482        if (i > 0) {
483            // Don't try to reclaim resource for the first time.
484            if (!mResourceManagerService->reclaimResource(resources)) {
485                break;
486            }
487        }
488
489        sp<AMessage> response;
490        err = PostAndAwaitResponse(msg, &response);
491        if (err != OK && err != INVALID_OPERATION) {
492            // MediaCodec now set state to UNINITIALIZED upon any fatal error.
493            // To maintain backward-compatibility, do a reset() to put codec
494            // back into INITIALIZED state.
495            // But don't reset if the err is INVALID_OPERATION, which means
496            // the configure failure is due to wrong state.
497
498            ALOGE("configure failed with err 0x%08x, resetting...", err);
499            reset();
500        }
501        if (!isResourceError(err)) {
502            break;
503        }
504    }
505    return err;
506}

首先,生成一个AMessage的心的强引用计数msg,AMessage的构造函数的两个参数,一个是枚举值kWhatConfigure(“init”),在MediaCodec.h中定义,另一个参数应该是AHandler类型,而MediaCodec就是AHandler的子类。现在来看一下是否是video(mIsVideo),如果是,要从参数format中取出视频的width和height,分别把值赋给类属性mVideoWidth和mVideoHeight;获取旋转角度,赋值给mRotationsDegrees,如果不存在该属性,就把mRotationsDegrees设成0度。如果encoder,那么宽高相乘要小于INT32_MAX的四分之一。一次设置msg的format,flags和surface。如果参数crypto不为NULL,设置msg的crypto。把msg赋值给类属性mConfigrueMsg。判断是否安全编码,把结果保存在type中;判断是video还是audio,结果保存在subtype中。生成两种MediaResource,根据type,subtype,另一种根据MediaResource::kGraphicMemory,把他们都放入之前生命的MediaResource类型的Vector resources中。进行一个循环,除了第一个循环,每次循环开始,都需要reclaim resource。调用PostAndAwaitResponse(msg, &response)(这会调用AMessage类中的同名方法,最终通过ALooper发送消息),发送msg,并且把响应存储到response,把返回值存储到err,如果err不等于NO_MEMORY,跳出循环。最后,返回err的值。

  • start
    下面,我们来看看start状态。
status_t MediaCodec::start() {
568    sp<AMessage> msg = new AMessage(kWhatStart, this);
569
570    status_t err;
571    Vector<MediaResource> resources;
572    MediaResource::Type type = (mFlags & kFlagIsSecure) ?
573            MediaResource::kSecureCodec : MediaResource::kNonSecureCodec;
574    MediaResource::SubType subtype =
575            mIsVideo ? MediaResource::kVideoCodec : MediaResource::kAudioCodec;
576    resources.push_back(MediaResource(type, subtype, 1));
577    // Don't know the buffer size at this point, but it's fine to use 1 because
578    // the reclaimResource call doesn't consider the requester's buffer size for now.
579    resources.push_back(MediaResource(MediaResource::kGraphicMemory, 1));
580    for (int i = 0; i <= kMaxRetry; ++i) {
581        if (i > 0) {
582            // Don't try to reclaim resource for the first time.
583            if (!mResourceManagerService->reclaimResource(resources)) {
584                break;
585            }
586            // Recover codec from previous error before retry start.
587            err = reset();
588            if (err != OK) {
589                ALOGE("retrying start: failed to reset codec");
590                break;
591            }
592            sp<AMessage> response;
593            err = PostAndAwaitResponse(mConfigureMsg, &response);
594            if (err != OK) {
595                ALOGE("retrying start: failed to configure codec");
596                break;
597            }
598        }
599
600        sp<AMessage> response;
601        err = PostAndAwaitResponse(msg, &response);
602        if (!isResourceError(err)) {
603            break;
604        }
605    }
606    return err;
607}

首先以kWhatStart状态创建一个AMessage的强引用计数msg。同configure一样,type和subtype一样,分别表示是否安全编码和音频或视频编码。分别以type和subtype创建MediaResource,存入向量resources中。在循环之中,如果不是首次循环,需要对资源进行回收再利用。每次循环都要进行reset。如果reset失败,终止循环。调用PostAndAwaitResponse(mConfigureMsg, &response)发送消息并等待返回,mConfigureMsg在configure阶段已经赋值。如果返回值不等于OK,终止循环。调用PostAndAwaitResponse(msg, &response)发送消息并等待返回。如果返回值不是NO_MEMORY,跳出循环。最后,函数返回最后设置的err的值。

  • dequeueInputBuffe
    现在来看看输入缓冲区的出列处理。
status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
748    sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, this);
749    msg->setInt64("timeoutUs", timeoutUs);
750
751    sp<AMessage> response;
752    status_t err;
753    if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
754        return err;
755    }
756
757    CHECK(response->findSize("index", index));
758
759    return OK;
760}

同样是,生成AMessage的强引用计数msg和response。调用PostAndAwaitResponse(msg, &response))发送消息并等待返回。如果返回不是OK,就把返回的error code作为函数的返回值。否则,返回OK。

  • queueInputBuffer
    将数据压入输入缓冲区队列。
status_t MediaCodec::queueInputBuffer(
689        size_t index,
690        size_t offset,
691        size_t size,
692        int64_t presentationTimeUs,
693        uint32_t flags,
694        AString *errorDetailMsg) {
695    if (errorDetailMsg != NULL) {
696        errorDetailMsg->clear();
697    }
698
699    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
700    msg->setSize("index", index);
701    msg->setSize("offset", offset);
702    msg->setSize("size", size);
703    msg->setInt64("timeUs", presentationTimeUs);
704    msg->setInt32("flags", flags);
705    msg->setPointer("errorDetailMsg", errorDetailMsg);
706
707    sp<AMessage> response;
708    return PostAndAwaitResponse(msg, &response);
709}

首先,创建AMessage的强引用计数msg,用参数index,offset,size,presentationTimeUs,flags,errorDetailMsg来初始化msg的index,offset,size,timeUs,flags,errorDetailMsg。PostAndAwaitResponse(msg, &response)发送并等待消息返回,并且,用这个调用的返回值作为函数的返回值。

  • stop
    stop的代码比较简单。
status_t MediaCodec::stop() {
610    sp<AMessage> msg = new AMessage(kWhatStop, this);
611
612    sp<AMessage> response;
613    return PostAndAwaitResponse(msg, &response);
614}

同样是,生成AMessage的强引用计数msg和response。调用PostAndAwaitResponse(msg, &response))发送消息并等待返回。

  • reset
    reset的代码如下:
status_t MediaCodec::reset() {
654    /* When external-facing MediaCodec object is created,
655       it is already initialized.  Thus, reset is essentially
656       release() followed by init(), plus clearing the state */
657
658    status_t err = release();
659
660    // unregister handlers
661    if (mCodec != NULL) {
662        if (mCodecLooper != NULL) {
663            mCodecLooper->unregisterHandler(mCodec->id());
664        } else {
665            mLooper->unregisterHandler(mCodec->id());
666        }
667        mCodec = NULL;
668    }
669    mLooper->unregisterHandler(id());
670
671    mFlags = 0;    // clear all flags
672    mStickyError = OK;
673
674    // reset state not reset by setState(UNINITIALIZED)
675    mReplyID = 0;
676    mDequeueInputReplyID = 0;
677    mDequeueOutputReplyID = 0;
678    mDequeueInputTimeoutGeneration = 0;
679    mDequeueOutputTimeoutGeneration = 0;
680    mHaveInputSurface = false;
681
682    if (err == OK) {
683        err = init(mInitName, mInitNameIsType, mInitIsEncoder);
684    }
685    return err;
686}

reset首先调用release释放codec。如果mCodec(CodecBase类型)不为NULL,如果mCodecLooper也不为NULL,首先对mCodecLooper反注册Handler,否则,对mLooper反注册Handler,并且把mCodec置为NULL。接下来进行一系列重新赋值操作,都赋值为初始化值。如果err等于OK,调用init进行初始化。

  • release
    我们看看reset中调用,并且可以做为一个独立状态的release。
status_t MediaCodec::release() {
647    sp<AMessage> msg = new AMessage(kWhatRelease, this);
648
649    sp<AMessage> response;
650    return PostAndAwaitResponse(msg, &response);
651}

同样是,生成AMessage的强引用计数msg和response。调用PostAndAwaitResponse(msg, &response))发送消息并等待返回。

libstagefright 框架设计分析 (1): ALooper, AHandler 看到哪里, 写到哪里, 也有可能烂尾,写这个主要是自己的总结,如果有人看不懂我想也是正常的。 android 5.0 之后player 主要就是Nuplayer, 所以awesome player 所使用的OMXCodec就不在使用了, 主要就是使用ACodec了。 所以libstagefright的decode过程大概就是 MediaCodec.cpp -> ACodec.cpp->OMX. 阅读详情

相关推荐

stagefright 架构分析(七) 动态加载libstagefrighthw.so

在加载软解码库的代码中,可以看到有加载libstagefrighthw.so的代码,其是加载硬件解码plugin,每个平台都有自己libstagefrighthw.so的实现,以实现硬编解码,以高通平台为例: OMXMaster::OMXMaster()     : mVendorLibHandle(NULL) { //加入硬解码Plugin     addVendorPlugin

flyingqr的专栏 6416

Android Stagefright框架

1.Video Playback的流程 在Android上,预设的多媒体框架(multimedia framework)是OpenCORE。OpenCORE的优点是兼顾了跨平台的移植性,而且已经过多方验证,所以相对来说较為稳定;但是其缺点是过於庞大复杂,需要耗费相当多的时间去维护。从Android 2.0开始,Google引进了架构稍為简洁的Stagefright,并且有逐渐取代OpenCORE的...

weixin_30576827的博客 386

Android MediaRecorder框架深度解析:从API调用到底层硬件编码

在Android音视频开发中,录制功能是核心需求之一,其背后依赖的多媒体框架涉及复杂的层级结构与数据流。理解其工作原理需要从基础概念切入:Android系统通过分层架构(应用层、Native框架层、服务与硬件抽象层)来管理多媒体任务,其中OMX(OpenMAX Integration Layer)组件是实现硬件加速编解码的关键接口。这一设计使得应用能够高效利用芯片厂商提供的硬件编码器,显著提升性能并降低功耗。在工程实践中,开发者通过MediaRecorder API进行录制,数据会经历采集、编码、复用等流程

weixin_34357436的博客 281

android libstragefright

理解与使用 Android libstagefright 库 在 Android 开发中,libstagefright 是一个用于处理媒体文件的多媒体框架,它为音频和视频编解码提供了强大的支持。接下来,我将为你提供一个实现 Android libstagefright 的完整指南,包括每一步所需的代码、说明和流程图。 实现...

weixin_40314524的博客 424

采购订单定价策略 超详细

‘需求’字段里填写的是一个例程编号(例程是一段判断程序),如果我们在需求中维护了例程编号,那只有这个例程条件被满足时,这个条件表才会被系统自动读取。一部分的条件表只有在凭证项目满足一定的条件时才会被读取。 ‘排斥的’字段如果别勾中的话,那表明如果这个条件表被系统成功读取到数据后,系统将停止这个存取顺序的执行,否则,系统将继续执行下去。 注意:a、如果我们给某个条件类型设定了有效期的话,那我们必须给这个条件类型分配一个存取顺序。 b、不能给一个抬头条件分配存取顺序。 是...

竹大的博客 1万+

DNF单机版搭建全攻略:从零开始到局域网联机(含常见问题解决)

本文提供详细的DNF单机版搭建指南,从环境配置到局域网联机设置,涵盖服务端安装、数据库配置及常见问题解决方案。适合新手和有经验的玩家,帮助快速搭建专属游戏服务器,重温经典游戏体验。

weixin_30832143的博客 847

安卓libStagefright高危漏洞分析

-序- 本周早些时候, 国外安全公司Zimperium爆出多处Android系统漏洞, 瞬间占据各大媒体头条. 号称一条彩信即可控制手机, 影响范围从Android 2.2到5.1通杀, 号称Android上的”心脏滴血”. 本着不放过任何漏洞细节的原则, 扒了一下相关信息. 要做这篇分析报告的时候, 一开始是拒绝的, 后来各种夸张新闻刷屏, 真的有这么恐怖吗? -漏洞信息初探- 

___leng的专栏 5110

《玩儿起来吧》MATLAB 实时图像处理系列(一二三四)

大家学了图像处理之后,是不是就想学以致用。而要用于生活中,最好的方式就是和摄像头搭配,实现实时图像处理。今天就给大家分享MATLAB实时图像处理的基础方法,希望能帮你快速入门。

HPC_ZY 1万+

android Stagefright框架

作为Android音视频开发人员,学习Stagefright框架需要结合理论、源码分析和实践验证。

WPP BLOG 895

如何编写 Skill 实现自动化用例生成

Skill 本质上是一组结构化的指令和工具定义,它告诉 AI 模型在特定场景下应该遵循什么流程、调用哪些工具、输出什么格式的内容。元信息:名称、描述、版本等。指令(Instructions):定义 AI 的行为准则和任务流程。工具(Tools):AI 可以调用的外部能力,如文件读写、代码执行、API 请求等。上下文(Context):预设的输入输出格式或示例。编写一个用于自动生成测试用例的 Skill,核心在于定义清晰的指令流程和输出规范。

Cesare的博客 2697

《干货满满!AI 应用架构师打造 AI 驱动生产计划的实战技巧》

生产计划是制造企业的“大脑”——它决定了买什么物料、产多少产品、用什么设备、什么时候交货,直接影响企业的成本、效率和客户满意度。需求预测靠经验,误差常超20%;排产依赖Excel,调整一次要3天;供应链协同靠邮件,物料延迟导致停产屡见不鲜。作为AI应用架构师,我们的任务是用AI重新“激活”这个“大脑”——用数据驱动的预测替代经验判断,用智能优化替代人工排产,用实时协同替代被动响应。本文结合3个制造业实战案例5段可运行代码2个数字孪生模型,从架构设计→核心模块实现→落地踩坑。

移动开发前沿的博客 1083

成功编译ffmpeg的android硬解码模块libstagefright

准备环境: CentOS6.4 (64位) ffmpeg源码: git clone git://source.ffmpeg.org/ffmpeg.git ffmpeg android NDK: http://developer.android.com/tools/sdk/ndk/index.html 将ffmpeg/tools/build_libstagefright脚本移到f

lidedongsn 9032

基于物联网技术设计的微型消防小车控制系统(STM32+ESP8266局域网)(181)

基于物联网技术的小微型消防车控制系统。通过远程控制功能,用户能够灵活操作消防车的行驶,方便快速抵达火灾现场。环境感知功能实时采集烟雾浓度、温湿度信息,为灭火决策提供科学依据。火源定位功能采用自动和手动两种模式,准确迅速地定位火源,并通过报警提示用户。灭火功能则根据火源定位结果,自动或手动启动干粉灭火器进行高效灭火。这些功能的结合实现了对小型火灾的全面应对,提升了消防效率与安全性,减少了火灾带来的损失。通过本项目的设计与实现,为消防领域提供了一种创新且实用的解决方案。

3081

关于libStagefright系列漏洞分析

百度安全攻防实验室 · 2015/07/29 18:100x00 前言文章对应着CVE-2015-{1538,1539,3824,3826,3827,3828,3829}7个CVE,具体映射关系目前不明。此次安全漏洞号称影响“95%”安卓手机的安全。通过跟进此次漏洞的攻击面来看,这种说法毫不夸张,外界报道的关于一个彩信就直接打下机器的说法也是可信的。但这也仅仅是众多攻击面中的一条而已。0x01 攻...

weixin_34130269的博客 648

Python爬虫实战:高效爬取中国版权登记数据

本文将详细介绍如何使用Python最新技术栈构建一个高效、稳定的中国版权登记数据爬虫系统。我们将使用Playwright+Asyncio实现无头浏览器自动化,结合Redis分布式任务队列和MongoDB数据存储,打造一个能够应对反爬机制的企业级数据采集方案。文章包含完整代码实现、性能优化技巧以及法律合规建议,帮助开发者快速掌握大规模数据采集的核心技术。本文详细介绍了2024年使用Python最新技术栈构建中国版权登记数据爬虫的完整方案。基于Playwright的无头浏览器自动化。

2201_76125261的博客 1899

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

LibStageFright

libmedia和libmediaplayerservice分析  1) libmedia目录在frameworks/base/media/libmedia,为多媒体底层库,这里面的内容被 编译成libmedia.so在整个MediaPlayer运行中属于Client部分  1.libmedia.so处于核心位置,主要实现对上层以及下层的接口类。对上层主要提供MediaPlayer类的接口,

Gabby 3718

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

文件上传与目录穿越漏洞实战解析:从原理到链式利用

文件上传漏洞和目录穿越漏洞是Web安全中最基础也最危险的两类服务端逻辑漏洞。其本质在于服务端对用户可控输入(如文件名、路径参数)缺乏严格校验与标准化处理,导致攻击者可上传恶意脚本或越权访问系统文件。理解Apache/Nginx等服务器的文件解析优先级、PHP的路径规范化机制及`realpath()`等关键函数行为,是识别绕过手法的核心。技术价值体现在无需0day即可实现RCE或敏感信息泄露,广泛应用于HW行动、渗透测试与红队评估。本文基于DVWA与自建靶机,聚焦真实代码逻辑、四层校验绕过、六种穿越载荷对比及

weixin_30632883的博客 808

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

android stagefright 编码 h264,ffmpeg编译android硬解码支持库libstagefright(1)——git

/opt/android-ndk-r8c/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld:jni/libavcodec.a(libstagefright.o): in fu...

weixin_42408094的博客 358

android StageFright框架解读

android StageFright框架解读 android多媒体框架 MediaPlayerService Stagefright OpenCore 底层openmax 1、JAVA类的路径: frameworks/base/media/java/android/media/MediaPlayer.java JAVA本地调用部分(JNI): frameworks/base/

keen_zuxwang的博客 4940

【Android 10 源码】深入理解 MediaCodec 组件分配

MediaCodec 系列文章: 【Android 10 源码】深入理解 MediaCodec 硬解码初始化 【Android 10 源码】深入理解 Omx 初始化 【Android 10 源码】深入理解 codecservice 启动 【Android 10 源码】深入理解 software Codec2 服务启动 【Android 10 源码】深入理解构建 MediaCodec 列表:buildMediaCodecList 组件分配的起点位于 native MediaCodec 的 init 流程。

洪伟的专栏 3521

[原]ffmpeg编译android 硬解码支持库 libstagefright

最近花了一天时间将ffmpeg/tools/build_stagefright执行成功,主要是交叉编译所需要的各种动态库的支持没链接上,导致各种报错,基本上网络上问到的问题我都碰到了,特此记录下来。   编译环境:Ubuntu 14.04 + Android NDK + FFmpeg源码。 第一步:将ffmpeg/tools/build_stagefright移动到ffmpeg下面,修改执行...

weixin_33883178的博客 348

libstagefright openmax编解码数据流向分析

OMXCodec::read第一次调用时,mInitialBufferSubmit值为true,这个标志就是用来标识OMXCodec::read是否是第一次调用的。 在if (mInitialBufferSubmit) { },中 1、把标志mInitialBufferSubmit置为false, 2、调用drainInputBuffers,把输入通道中的所有输入缓存区,逐个传递给drain

u011204007的专栏 1675

exoplay切换全屏_如何使用rotation-degrees手动旋转(rotate)exoplayer2播放器

效果展示旋转前的显示:image.png旋转处理后的效果:image.png如何实现方法一. 最直接的想法是进行横竖屏切换.Activity变成横屏显示了, 视频播放自然也就全屏了. 没什么可细说的.这个做法也有缺点: 有些情况下某些页面不允许进行横竖屏切换, 或者横竖屏切换后, 仍然要保持其他显示元素与竖屏时一样. 我们现在这个产品的需求就是这么要求的. 那就需要另想办法了.方法二...

weixin_39842955的博客 1686

stagefright媒体框架

简单的播放步骤 1、  MediaPlayer mediaPlayer = new MediaPlayer(); 2、  mediaPlayer.setDataSource(“/mnt/usbhost1/123.mp4”); 3、  mediaPlayer.prepare(); 4、  mediaPlayer.start(); 5、  mediaPlayer.stop();    

袁家二少的专栏 1269

android stagefright 框架

原文:【转】android stagefright 框架 链接:http://blog.chinaunix.net/uid-9838896-id-2976618.html 在Android上,预设的多媒体框架(multimedia framework)是OpenCORE。OpenCORE的优点是兼顾了跨平台的移植性,而且已经过多方验证,所以相对来说较为稳定;但是其缺点是过於庞大

xww810319的专栏 4047
上一篇: Android 多媒体框架(2)—— android_media_MediaCodec.cpp源码分析
下一篇: Android多媒体框架(4)—— AMessage,AHandler和ALooper源码分析
hunter800421
博客等级 码龄22年 60粉丝 97原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值