图像运动检测系统

运动图像检测系统:
准备:
移植Linux2.6.29运行于s3c2440板子上,
按键驱动,
USB host controller驱动
声卡驱动
动态链接文件系统制作SDK-MOTION/src/fs/rootfs-motion.tar.gz
madplay播放器移植
图像运动检测程序设计
报警主程序设计




1.当移动物体进入监控范围,系统报警
2.系统报警后保存移动物体图像
3.报警时播放一段指定音乐,mp3,avi格式
4.三分钟内检测到连续变化的次数超过20次,认为是在下雨,检测系统暂停2小时,2小时后重启。


有两种方式触发报警:
1.外部按键中断
2.图像有变化触发报警


ls 
app driver fs kernel shell
tar zxvf linux2.6.29.tar.gz
cd linux2.6.29
make clean
cp config-motion .config
该.config文件采用NFS起根文件系统,且已经选择了声卡驱动,网眼V2000摄像头驱动。修

改.config的CMDLINE行的IP配置,NFS起根文件系统。
make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-配置内核
编译内核,解压根文件系统。

按键驱动移植:
SDK-MOTION/src/driver/button目录,采用上升沿触发方式。
cd motion/src/driver
make clean;make
生成mini2440_buttons_rise.ko



参考madplay移植到mini2440.doc
移植完后cp madplay /nfsroot/rootfs-motion


检测程序设计:
运动图像检测最常用图像检测方法是将当前帧和前一帧(背景帧)比较,如果不同像素的点数超过阀

值,则认为图像有变化。
本项目图像检测程序采用开源软件motion:
http://www.lavrsen.dk/foswiki/bin/view/Motion/WebHome
参考motion-3.2.11.1.tar.gz,motion的使用.doc
将motion和motion.conf拷贝到文件系统/nfsroot/rootfs-motion/motion
拷贝脚本文件:
报警文件11.mp3,22.mp3
count.txt用于存放计算/root/motion中图像数
key_pic_motion总的程序运行脚本
appon(motion.conf中on_event_start本)
appoff(motion.conf中on_event_end)
mapplay(播放器)
pic.txt(记录是否有图像运动被检测到)
cd /home/motion/src/shell
cp -a * /nfsroot/rootfs-motion/motion






报警主程序设计:
程序中使用定时器,每三分钟比较图像连续变化次数是否超过20次,如果超过则添加一个2小时定时

器,在这2小时如有图像变化不处理,如果没有超过则添加下一个3分钟定时器。
报警主程序为app-motion.c
cd /home/motion/src/app
make clean;make
cp app-motion /nfsroot/rootfs-motion/motion


项目测试一:
NFS方式起根文件系统,要求文件系统使用动态链接的文件系统。
cd /motion
./key_pic_motion
该脚本包括按键驱动加载,检测程序运行,主程序运行。

测试按键中断:
当按键被按下时,[type1]button detect, button_value:4
                [key]Now detect **key** have change
表明主程序检测到键值4被按下,开始报警。
[key]No,have not mp3 play  表明目前系统中没有mp3播放,
[key]Start add 3 minute alarm! 第一次检测时,添加一个3分钟定时器。
THIS SONG IS 11.mp3 报警铃声是11.mp3
Now con_cnt=0表明当前连续变化的次数为0

测试项目二:测试运动图像
[key2]pic motion detect!表明检测到图像运动。
Now con_cnt=2表明目前检测到图像连续变化的次数是2。

测试项目三:
3分钟到达20次以上时
[pic]3alarm! con_cnt=23
Now maybe it is raining! system will sleep 7200 seconds!

测试项目四:
系统处于睡眠状态时,有外部中断或者图像运动时会提示:
It is raining! system is sleeping!
summary time:7200 seconds, sleep:157 seconds,nees sleep:7043 seconds



代码分析:
主要程序是app-motion.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <time.h>

/*
1. 检测到图像变化,报警
2. 检测到外部中断,报警
3. 3分钟能连续检测图像变化20次,则暂停2小时,2小时后在开启
*/
/*define globe variable*/

/*play_pid:当前播放的MP3子进程ID*/
unsigned int play_pid = 0;

/*gradchild:当前播放的MP3孙子进程ID*/
unsigned int gradchild = 0;

unsigned int play_flag;

/*共享内存描述标记
sharemem:
 byte1:孙子进程ID号
 byte2:是否有MP3播放标识play_flag_2
 //byte3:MP3播放次数
*/
int shmid;
char *p_addr;

#define PERM S_IRUSR|S_IWUSR 



/*报警铃声歌曲名,song1图像变化报警铃声
song2外部中断报警铃声*/
char *song1="11.mp3";
char *song2="22.mp3";
//char *song="234.mp3";


/*定时器时间为3分钟*/
#define THREE_ALARM 3*60
/*睡眠时间为2小时*/
#define SLEEP_TIME 2*60*60
#define  CPM_CNT 20
int threemin_alarm = 1;

static int con_cnt=0;
int sleep_flag = 0;
unsigned int time_tmp;

#define max(flag) (flag) >1 ? "pic":"key"
//#define DEBUG
int alarm_flag;

static int pic_cnt;
int cnt_fd;
/*计算图像变化次数,超过2次则认为有图像变化*/
#define COMPARE_CNT 5


/*************************************************
Function name: count_pic
Called by         : 函数main
Parameter    : void
Description     : 计算图片变化数
Return           : int
Autor & date : ykz 10.4.25
**************************************************/
int count_pic(void)
{
    int fd,ret;
    char *buf;
    buf = (char *)malloc(10);

    system("ls /root/motion | wc -l > count.txt");
    lseek(cnt_fd, 0 ,SEEK_SET);
    ret = read(cnt_fd , buf , 10);
    if(ret)
      ret = atoi(buf);
    
    free (buf);
    return ret;
}


/*************************************************
Function name: my_func_sleepalarm
Called by         : 函数my_func_3alarm
Parameter    : sing_no
Description     : 3分钟连续变化20次后,延时2小时后的唤醒函数
Return           : void
Autor & date : ykz 10.4.25
**************************************************/
void my_func_sleepalarm(int sign_no)
{

    if( sign_no == SIGALRM){
        printf("Now sleep time finished!start a new work!\n");
        /*则睡眠表示sleep_flag为0*/
        sleep_flag = 0;
    }//end sing_no
}


/*************************************************
Function name: my_func_3alarm
Called by         : 函数restart_caculate_play
Parameter    : sing_no
Description     : 每隔3分钟检测图像连续变化是否超过20次
Return           : void
Autor & date : ykz 10.4.25
**************************************************/
void my_func_3alarm(int sign_no)
{

    if( sign_no == SIGALRM){

        struct tm *p;
        time_t timep;
        unsigned int second;
      printf("\n------------------warning------------------\n");
      printf("[%s]3alarm!con_cnt=%d\n",max(alarm_flag),con_cnt);
      /*判断图像运动次数是否超过20次
       如果是:则系统睡眠2小时;
       不是,则系统继续添加下一个3分钟的定时器*/
      if( con_cnt >= CPM_CNT){
          if(play_flag){
            kill(play_pid,SIGKILL);
            kill(gradchild,SIGKILL);
            wait(NULL);
          }
          //sleep (1);
        time(&timep);
        p = localtime(&timep);
        second = (p->tm_hour)*60*60 + (p->tm_min) * 60 + p->tm_sec;
        time_tmp=second;

          printf("Now maybe it is rainning!,system will sleep %d seconds!\n",SLEEP_TIME);
          con_cnt = 0;
          /*置睡眠标识sleep_flag为1*/
          sleep_flag = 1;
          /*系统睡眠2小时*/
          if(signal(SIGALRM,my_func_sleepalarm)<0)
            perror("signal");
          alarm(SLEEP_TIME);
     }
     else{
       printf("Nornal test!not rainning!\n");
       con_cnt = 0;
       /*添加下一个3分钟的定时器*/
       if(signal(SIGALRM,my_func_3alarm)<0)
          perror("signal");
       alarm(THREE_ALARM);          
     }

      printf("\n------------------warning------------------\n");
    } //end sing_no
}



/*************************************************
Function name: play
Called by         : 函数startplaymp3
Parameter    : void
Description     : 子进程创建孙子进程播放MP3
Return           : void
Autor & date : ykz 10.4.2
**************************************************/
void play(void)
{

    pid_t fd;
    char *c_addr;
    char *song_name;
    int play_flag_gradchild1=1;
    int play_flag_gradchild2=2;
    int i=0;

        /*创建孙子进程*/
        fd = fork();
        if(fd == -1)
        {    
            perror("fork");
            exit(1);
        }
        else if(fd == 0) /*孙子进程,播放MP3*/
        {

            printf("\n--------------play mp3----------------\n");
            if(alarm_flag==1)
              song_name=song1;
            else song_name=song1;
            printf("THIS SONG IS %s\n",song_name);
            /*使用madplay播放MP3*/
            execl("/motion/madplay","madplay",song_name,NULL);
            printf("\n\n\n");
        }
        else  /*子进程*/
        {
        
            /*把孙子进程的id传入共享内存*/
            memcpy(c_addr,&fd,sizeof(pid_t));
            
            /*目前在播放MP3,将播放标记传入共享内存*/
            memcpy(c_addr+sizeof(int),&play_flag_gradchild1,4);
            
            /*等待孙子进程结束,只要结束:
            传回play_flag_gradchild2=2,表示现在MP3没有播放*/
            if(fd == wait(NULL))
            {
                printf("\n------------------warning------------------\n");
                
            /*通过共享内存传回play_flag_gradchild2=2,表明后面的一定不是连续的MP3播放*/
                    memcpy(c_addr+sizeof(int),&play_flag_gradchild2,4);
                    printf("Gradchild normal finish!\n");
              printf("------------------warning------------------\n");
            }//end if(fd == wait(NULL))
        }

}



/*************************************************
Function name: startplaymp3
Called by         : 函数caculate_play,restart_caculate_play
Parameter    : pid_t *childpid
Description     : 主进程创建子进程
Return           : void
Autor & date : ykz 10.4.2
**************************************************/
void startplaymp3(pid_t *childpid,int flag)
{

        int ret = 0;
      pid_t cun_pid;
        /*创建子进程*/
        cun_pid = fork();
        
        if(cun_pid == -1)
        {    
            perror("son fork");
            exit(1);
        }

        if(cun_pid == 0) /*子进程*/
          play();
        

        if(cun_pid > 0) /*父进程*/
        {
            *childpid = cun_pid;
          sleep(1);  /*让孙子进程先执行*/
          
          /*如果是图像运动变化,将全局变量con_cnt加1*/
          if( flag == 2 )
            con_cnt++;
          
          printf("\nNow con_cnt=%d\n",con_cnt);
          /*把孙子进程的pid传给父进程*/
          memcpy(&gradchild,p_addr,sizeof(pid_t));

        }

}

/*************************************************
Function name: caculate_play
Called by         : 函数key_pic_mp3
Parameter    : int flag
Description     : 连续播放时,计算连续播放时间,调用函数startplaymp3开始新的播放
Return           : int
Autor & date : ykz 10.4.2
**************************************************/
int caculate_play(int flag)
{

    int ret;
       
    /*kill掉当前播放MP3的子进程,孙子进程*/
        
        kill(play_pid,SIGKILL);
        kill(gradchild,SIGKILL);
        wait(NULL);
      
    /*将共享内存清空*/
        memset(p_addr,'\0',1024);

     startplaymp3(&play_pid , flag);
   return 1;
}


/*************************************************
Function name: restart_caculate_play
Called by         : 函数key_pic_mp3
Parameter    : int flag
Description     : 未超过连续播放时间时,有新的图像或者外部中断检测到时调用函数
Return           : void
Autor & date : ykz 10.4.2
**************************************************/
void restart_caculate_play(int flag)
{

        int ret;
        /*add 3 minute alarm*/
        if(threemin_alarm){
            if(signal(SIGALRM,my_func_3alarm)<0)
                perror("signal");
            ret = alarm(THREE_ALARM);
            printf("[%s]Start add 3 minute alarm!\n",max(flag));
            threemin_alarm = 0;
        }


#if 1 
    /*判断是否有子进程,或者孙子进程,如果有则KILL掉*/
        if(play_flag){
            kill(play_pid,SIGKILL);
            kill(gradchild,SIGKILL);
            wait(NULL);
            //sleep(1);
        }
#endif
        play_pid = 0;
        gradchild = 0;
        
        memset(p_addr,'\0',1024);
    
      /*开始播放MP3*/
      startplaymp3(&play_pid,flag);
}



/*************************************************
Function name: key_pic_mp3
Called by         : 函数main
Parameter    : int flag
Description     : 当检测到有外部中断,或者图像变化时处理函数
Return           : int
Autor & date : ykz 10.4.2
**************************************************/
int key_pic_mp3(int flag)
{

  printf("------------------------- KEY_PIC_MP3 ----------------------------\n");
    printf("[%s]Now detect ** %s ** have change\n",max(flag),max(flag));
    int ret = 0;
    int over_flag_2; 
    int play_flag_2;

  /*sleep_flag 用于判断,检测系统是否处于2小时的睡眠状态*/
    if(sleep_flag){
        printf("It is rainning!system is sleeping!\n");
        struct tm *p; 
        time_t timep;
        unsigned int second;
        
        time(&timep);
        p = localtime(&timep);
        second = (p->tm_hour)*60*60 + (p->tm_min) * 60 + p->tm_sec;

        printf("summary time:%d seconds,sleep:%d seconds,need sleep:%d seconds\n",
                    SLEEP_TIME,second-time_tmp,SLEEP_TIME-(second-time_tmp));

        return 0;
    }
    
    alarm_flag = flag;


    /*从sharemem中读出是否有MP3处在播放状态标识*/
    memcpy(&play_flag_2,p_addr + sizeof(int),sizeof(int));
//    printf("===>key_pic_mp3,play_flag_2=%d\n",play_flag_2);
    
    play_flag = play_flag_2;
    /*play_flag_2:当前是否有MP3在播放
    0:当前没有MP3播放
  1:子进程当前处在MP3播放状态
  2:孙子当前播放MP3正常结束,且当前没有MP3播放*/

    /*当前有MP3在播放*/
    if(play_flag_2 == 1){
            printf("[%s]Yes,have mp3 play\n",max(flag));
            /*调用*/
            ret = caculate_play(flag);
    }
    
    /*当前无MP3在播放*/
    else{
            printf("[%s]No, have not mp3 play\n",max(flag));        
           restart_caculate_play(flag);
    }
    
    
        return ret;
}



/*************************************************
Function name: main
Called by         : 
Parameter    : void
Description     : 主函数,检测按键是否有按下,通过pic.txt检测图像是否有变化
Return           : int
Autor & date : ykz 10.4.2
**************************************************/
main(void)
{
    
     int buttons_fd,pic_fd;
     char pic_buf[1];
     int key_value;
     int flag;
     int ret;
     int tmp_cnt;
     /*设备文件的打开*/
     buttons_fd = open("/dev/buttons", 0);
     if(buttons_fd < 0) {
        perror("open device buttons");
        exit(1);
     }
     printf("open buttons sucess!\n"); 
     
     
     
     /*文件pic.txt记录是否有图像变化。
       1:有图像变化
       0:没有图像变化*/  
     pic_fd = open("pic.txt",O_RDWR | O_CREAT,0666);
     if(pic_fd < 0) {
        perror("open pic.txt");
        exit(1);
     }
     printf("open pic.txt success!\n");
    
     /*文件count.txt用于图像连续变化时,记录/root/motion中图片张数*/
     cnt_fd = open("count.txt",O_RDWR | O_CREAT,0666);
     if(cnt_fd < 0){
        perror("open count.txt");
        exit(1);
     }
     printf("open count.txt success!\n");
     system("ls > count.txt");
     
     /*共享内存申请*/
         if((shmid = shmget(IPC_PRIVATE,20,PERM))== -1)
            exit(1);
        p_addr = shmat(shmid,0,0);
        memset(p_addr,'\0',1024);
        
        
    /*主循环,首先判断是外部中断还是图像变化*/
    while(1){
    
        /*外部中断检测,监听获取键值*/
            ret = read(buttons_fd, &key_value, sizeof key_value);
            if (ret != sizeof key_value)         
                    perror("read buttons\n");     
            else {
                if(key_value){
                    printf("====================================================================\n");
                    printf("\n\n\n====================================================================\n");
                    printf("[type1]button detect,buttons_value: %d\n", key_value);
                    /*外部中断处理*/
                    key_pic_mp3(1);
                }
            } //end else
        
        
        
        /*图形变化检测,当有图像变化时motion会产生一个事件,
        事件处理为脚本/motion/appon,该脚本先点亮LED灯;
        然后向文件/motion/pic.txt写入字符"1"表明现在有图像变化被检测到;
        
        读取文件/motion/pic.txt第一个字符
         0:没有图像变化
         1:有图像变化*/
         
         lseek(pic_fd, 0 ,SEEK_SET);
         ret = read(pic_fd, pic_buf, 1);

       if(ret==1)
         {
            if(pic_buf[0] == '1'){ /*有图像变化被检测到*/
             printf("====================================================================\n");
             printf("\n\n\n====================================================================\n");
             printf("[type2]pic motion detect!\n");
             lseek(pic_fd, 0 ,SEEK_SET);
            
             if((ret = write(pic_fd, "0", 1)))
                  lseek(pic_fd, 0 ,SEEK_SET);
             
//             system("rm /root/motion/* -rf");
             pic_cnt = count_pic();
             //printf("first pic_cnt=%d\n",pic_cnt);
             /*图像运动变化处理*/
              key_pic_mp3(2);
         }
        
            else if(pic_buf[0] == '0'){
             tmp_cnt = count_pic();
             if( (tmp_cnt-pic_cnt) > COMPARE_CNT){
                printf("====================================================================\n");
                printf("\n\n\n====================================================================\n");
                printf("[type3]pic motion detect!\n");
            
//                system("rm /root/motion/* -rf");
                pic_cnt = count_pic();
                /*连续图像运动变化处理*/
                key_pic_mp3(2);
             }
            } //end else if
              
         }

        sleep (2);
              
      }//end while
      close (cnt_fd);
      close (pic_fd);
      close (buttons_fd);
    exit(0);    
}
该项目按键驱动程序:
mini2440_buttons_rise.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>

#define DEVICE_NAME     "buttons"

struct button_irq_desc {
    int irq;
    int pin;
    int pin_setting;
    int number;
    char *name;    
};

static struct button_irq_desc button_irqs [] = {
    {IRQ_EINT8 , S3C2410_GPG0 ,  S3C2410_GPG0_EINT8  , 0, "KEY0"},
    {IRQ_EINT11, S3C2410_GPG3 ,  S3C2410_GPG3_EINT11 , 1, "KEY1"},
    {IRQ_EINT13, S3C2410_GPG5 ,  S3C2410_GPG5_EINT13 , 2, "KEY2"},
    {IRQ_EINT14, S3C2410_GPG6 ,  S3C2410_GPG6_EINT14 , 3, "KEY3"},
    {IRQ_EINT15, S3C2410_GPG7 ,  S3C2410_GPG7_EINT15 , 4, "KEY4"},
    {IRQ_EINT19, S3C2410_GPG11,  S3C2410_GPG11_EINT19, 5, "KEY5"},
};
static int key_values = 0;

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

static volatile int ev_press = 0;


static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
    struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
    int down;
    // udelay(0);
    down = !s3c2410_gpio_getpin(button_irqs->pin);
    if (!down) { 
    //printk("rising\n");
    key_values = button_irqs->number + 1;
        ev_press = 1;
        wake_up_interruptible(&button_waitq);
    }
   else {
    //printk("falling\n");
    ev_press = 0;
    return 0;
   }
    return IRQ_RETVAL(IRQ_HANDLED);
}


static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
{
    int i;
    int err = 0;
    
    for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
    //printk("gpio setup\n");
    s3c2410_gpio_cfgpin(button_irqs[i].pin, button_irqs[i].pin_setting); //add by ykz
    if (button_irqs[i].irq < 0) {
        continue;
    }
     /* IRQ_TYPE_EDGE_FALLING,IRQ_TYPE_EDGE_RISING,IRQ_TYPE_EDGE_BOTH */
        //err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, 
        //                  button_irqs[i].name, (void *)&button_irqs[i]);
        err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_RISING, 
                          button_irqs[i].name, (void *)&button_irqs[i]);
        if (err)
            break;
    }

    if (err) {
        i--;
        for (; i >= 0; i--) {
        if (button_irqs[i].irq < 0) {
        continue;
        }
        disable_irq(button_irqs[i].irq);
            free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
        }
        return -EBUSY;
    }

    ev_press = 0;
    
    return 0;
}


static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
{
    int i;
    
    for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
    if (button_irqs[i].irq < 0) {
        continue;
    }
    free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
    }

    return 0;
}


static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
    unsigned long err;
    //int i=0;
#if 0
    if (!ev_press) {
    if (filp->f_flags & O_NONBLOCK)
        return -EAGAIN;
    else
        wait_event_interruptible(button_waitq, ev_press);
    }
#endif
    if(count != sizeof key_values)
    return -EINVAL;
    ev_press = 0;
    err = copy_to_user(buff, &key_values, sizeof(key_values));
    key_values = 0;
    return sizeof(key_values);
}

static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
{
    unsigned int mask = 0;
    poll_wait(file, &button_waitq, wait);
    //printk("poll\n");
    if (ev_press){
    //printk("==>read\n");
        mask |= POLLIN | POLLRDNORM;
    }
    return mask;
}


static struct file_operations dev_fops = {
    .owner   =   THIS_MODULE,
    .open    =   s3c24xx_buttons_open,
    .release =   s3c24xx_buttons_close, 
    .read    =   s3c24xx_buttons_read,
    .poll    =   s3c24xx_buttons_poll,
};

static struct miscdevice misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &dev_fops,
};

static int __init dev_init(void)
{
    int ret;

    ret = misc_register(&misc);

    printk (DEVICE_NAME"\tinitialized\n");

    return ret;
}

static void __exit dev_exit(void)
{
    misc_deregister(&misc);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值