JavaCV + FFmpeg 拉流播放器

视频抽帧实现 视频基本内容了解,视频抽帧实践 阅读详情

一、设计方案

一个简单的拉流播放器,需要具备以下功能:

  1. 将直播流拉取到设备上并展现出来
  2. 保证播放当前直播流的音视频是同步的
  3. 播放视频时可以切换全屏幕与非全屏

二、定义播放器的生命周期

在定义播放器的生命周期们需要做到以下两步:

1. 先定义一下播放器的事件   

2. 定义播放器展示的控件

1. 定义播放器事件

一个播放器,需要定义出来相应的播放器的事件,最基本的播放器的操作就是:播放、暂停、停止。示例代码如下:

/**
 * 播放器抽象类
 */
public abstract class Player {

    protected boolean play = false;

    public void play() {
        this.play = true;
    }

    public void pause() {
        this.play = false;
    }

    public void stop() {
        this.play = false;
    }
}

2. 定义SurfaceView来显示拉取的视频流

这里其实和原生的MediaPlayer及Ijkplayer是一样的,都是监听Surface的状态来控制播放器:创建,暂停,停止并release。

这里我们使用的控件是SurfaceView,创建一个VideoSurfaceView继承SurfaceView,并实现SurfaceHolder.Callback接口:

@Override
public void surfaceCreated(SurfaceHolder holder) {
    initLayout(mPlayer.getWidth(), mPlayer.getHeight());
    play();
    if (onPreparedListener != null) onPreparedListener.onPrepared();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    Log.v(TAG, "surfaceChanged...");
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    mPlayer.pause();
}

public void releasePlayer() {
    mPlayer.stop();
}

到这里我们把基本的播放器的生命周期的控制部分完成了,接下来就是完成基本的音视频数据的获取和播放了。

三、使用JavaCV + FFmpeg的API播放拉取音视频流

我们使用的是 JavaCV + FFmpeg的API,关于JavaCV的基本的介绍在上一篇文章 JavaCV入门 里面已经做了,下面一边介绍使用到的核心类一边说明音视频播放的流程:

1. FFmpegFrameGrabber

所在package包为:org.bytedeco.javacv,完整类名为:org.bytedeco.javacv.FFmpegFrameGrabber

FFmpegFrameGrabber可以理解为解码器,也可以理解为帧收集器,主要作用就是将视频流以帧的形式拉去到手机设备上。

mFrameGrabber = FFmpegFrameGrabber.createDefault(path);

上面的代码就是创建FFmpegFrameGrabber的方式,path就是要拉取流的地址。

mFrameGrabber.setPixelFormat(AV_PIX_FMT_RGBA);

设置帧收集时的像素格式,这块设置AV_PIX_FMT_RGBA的原因主要是,我们展示画面的时候是转换为Bitmap格式的。

mFrameGrabber.setOption("fflags", "nobuffer");

上面的代码表示我们可以像ijkplayer一样,设置一些参数,这些参数格式我们可以参考ijkplayer也可以去ffmpeg命令行的一些设置参数文档里面去查找,这里就不多赘述了。

mFrameGrabber.start();

上面的代码就是让帧收集器启动,这样就开始拉流了。

2. Frame

所在package包为:org.bytedeco.javacv,完整类名为:org.bytedeco.javacv.Frame

Frame 是一个用于管理音频和视频帧数据的类。 在CanvasFrame、FrameGrabber、FrameRecorder及他们的子类里面都有用到。

Frame grabframe = mFrameGrabber.grab(); 

上面的代码表示从帧收集器里面抓去最新的帧:

播放音频:grabframe.samples里面获取到的就是原始的pcm音频数据,交给AudioTrack处理就ok了。

播放视频:首先需要将Frame图像转换为Bitmap,AndroidFrameConverter.convert(frame)就可以转换,但是在这之前需要使用OpenCVFrameConverter.ToIplImage将抓出来的Frame转换一下。

Canvas canvas = mHolder.lockCanvas();
canvas.drawBitmap(bmp, null, new Rect(0, 0, canvas.getWidth(), frame.imageHeight * canvas.getWidth() / frame.imageWidth), null);
mHolder.unlockCanvasAndPost(canvas);

上面的代码表示将获取到的位图绘制到SurfaceHolder里面去,这里建议启动线程去绘制,这样效率会高很多。

javacv从入门到精通——第十三章javacv中FFmpegFrameGrabber的start方法执行时间过长,怎么优化? 如何优化javacv中FFmpegFrameGrabber的start方法执行时间 阅读详情

相关推荐

JavaCV与FFmpeg:音视频流处理技巧

JavaCV是一个开源的Java接口,为OpenCV、FFmpeg和其他类似工具提供了封装。它允许Java开发者直接在他们的应用程序中使用这些强大的本地库,而无需深入了解复杂的本地代码。JavaCV特别适用于处理图像和视频数据,提供了一系列的功能,如图像捕获、处理和视频编解码。FFmpeg是一个非常强大的多媒体框架,能处理几乎所有格式的音频和视频。它包括了一系列转码、流处理和播放的工具。在JavaCV中,FFmpeg被用于处理视频流的编码和解码。

qq_38334677的博客 6030

Spring Boot 多媒体(音频/视频)文件处理FFmpegFrameGrabber 方法(例子:获取视频总时长)

以上是FFmpegFrameGrabber类提供的一些常用方法,通过这些方法可以实现对多媒体文件的读取、解析和处理。备注:业务逻辑需要根据自己的情况更改。

qq_53722480的博客 6260

Dify工作流自动化:如何用Trae AI助手10倍提升你的开发效率(含避坑指南)

本文详细介绍了如何利用Trae AI助手自动化生成Dify工作流,实现开发效率的10倍提升。通过构建DSL示例库、精准需求描述和四步实战法,开发者可将重复的配置工作交给AI,快速生成可运行的工作流,并提供了关键的避坑指南。

weixin_29062865的博客 370

javacv从入门到精通进阶——第八章:视频流处理

这份javacv学习大纲主要分为入门、进阶和高级三个部分,其中入门部分包括了javacv的概述、环境搭建和基础应用;进阶部分则讲解了图像和视频处理、音频处理以及opencv的结合等内容;而高级部分则进一步深入了解了实时视频流处理、深度学习和人工智能等领域的应用。学习本大纲能够掌握使用javacv进行图像和视频处理、音频处理、实时视频流处理以及深度学习等领域的知识和应用。

ayou_llf的博客 1万+

无线Mesh组网全攻略:从原理、部署到调优与排障

对于布线困难的大户型、复式楼或园区,传统的无线中继往往面临带宽减半和链路不稳定的问题。无线Mesh网络通过多跳转发与自组织路由,让每个节点无需网线即可协作扩展覆盖,并依靠动态路径选择实现自愈。其核心技术涉及802.11s、HWMP路由协议,以及802.11k/v/r协同的无缝漫游,能有效提升终端切换体验。在实际部署中,设备选型、回程频段规划与布点位置直接决定网络质量。围绕无线Mesh的组网原理、设备选型、配置调优与故障排查,提供一套工程实践参考,帮助用户在复杂环境下构建稳定高效的无缝覆盖。

weixin_34098209的博客 271

基于UWB技术的自动跟随避障系统设计研究

基于UWB技术的自动跟随避障系统设计研究 摘要:目前我国针对自动跟随避障系统方面的研究较少,同时国内自动跟随避障系统仍然处在未成熟阶段,多数仍停留在应用蓝牙控制或摄像头捕捉这类跟随方式,但这类方式在实际应用过程中往往会出现易受干扰,精度差,易受环境干扰等问题,这不仅会降低自动跟随避障系统的性能,同时也会对用户造成诸多困扰。因此,就需要结合目前较为有效的UWB技术,对自动跟随避障系统进行设计。基于此,本文首先将会针对UWB技术展开概述,而后对基于UWB技术的自动跟随避障系统组成展开分析,进而对基于UWB技术的

qq_43543515的博客 7992

JavaCV 学习(二):使用 JavaCV + FFmpeg 制作拉流播放器

一、前言   在Android 音视频开发学习思路中,我们不断的学习和了解音视频相关的知识,随着知识点不断的学习,我们现在应该做的事情,就是将知识点不断的串联起来。这样才能得到更深层次的领悟。通过整理Android 音视频开发(一) : 通过三种方式绘制图片我们知道可以使用ImageView和SurfaceView甚至是View来展示图片,通过整理Android 音视频开发(...

weixin_30512043的博客 1371

在ROS中使用行为树

在ROS中使用行为树 我提供的工程示例文件: demo链接 文章目录在ROS中使用行为树0、 学习本教程的前提1、 behaviorTree 库安装(ROS使用下的安装方式)2、 处理 behaviortree 库并准备好 service 和 ros action2.1 对ROS 的 service 和 action 进行封装,使其在 behaviortree 上使用更加方便。2.2 创建一个 service 和一个 action2.3 创建与决策树相关的数据类型2.4 修改`package.xml`文件2

qq_37416258的博客 6375

Android 视频播放器 (三):使用NBPlayer播放直播视频

一、前言   在Android 音视频开发学习思路中,我们不断的学习和了解音视频相关的知识,随着知识点不断的学习,我们现在应该做的事情,就是将知识点不断的串联起来。这样才能得到更深层次的领悟。通过整理Android 音视频开发(一) : 通过三种方式绘制图片我们知道可以使用ImageView和SurfaceView甚至是View来展示图片,通过整理Android 音视频开发(...

weixin_30836759的博客 215

Acute逻辑分析仪TL4134E实战:从触发电平到协议解析的完整避坑指南

本文是一份针对Acute逻辑分析仪TL4134E的实战避坑指南。文章深入解析了触发电平分组、采样率与存储深度权衡、高级触发等核心概念,并提供了混合电压系统测量、eMMC调试内存优化、SPI/I2C/UART协议解析排错等具体配置技巧,帮助硬件工程师和嵌入式开发者高效捕获与分析数字信号,快速定位通信问题。

weixin_29207149的博客 404

hpm6750evk2开发板(一)菜鸟入门

hpm6750与我们平时常用的stm单片机在开发环境和开发方法上有很大的区别,所以上手还是需要花一点时间的,拿到板子先不要急着上电调试,我们先找资料学习一下了,免得多走冤枉路,以下是我整理的一些学习资源,包括开发板使用手册、芯片SDK、开发工具、视频资料等,建议先把使用手册和学习视频先看了,再取搭建环境测试。最近刚入手一块hpm6750evk2开发板,花了几天时间学习,算是菜鸟入门了,今天分享一下学习过程。看完资料后,把开发工具安装上,下一篇文章我们分享一下调试工程的过程和遇到的问题。

Silas1750的博客 3084

StringBoot 通过FFmpegFrameGrabber获取视频首帧作为图片展示

FFmpegFrameGrabber获取视频首帧作为图片展示

qq_46100517的博客 7294

HackRF实战:用Portapack扩展板玩转GPS信号模拟(附固件刷写教程)

本文详细介绍了如何使用HackRF与Portapack扩展板进行GPS信号模拟,包括固件刷写、信号生成与发射配置等实战操作。通过专业工具和优化技巧,读者可以掌握软件无线电的高级应用,解决手机定位难题,同时强调安全合规使用的重要性。

weixin_28717431的博客 362

FFmpegFrameGrabber视频抽帧工具类

通过视频链接进行关键帧抽取图片,核对利用FFmpegFrameGrabber对视频流进行抽帧处理。

共同见证AI,未来一起成长 5127

Open-Sora代码详细解读(1):解读DiT结构

目前开源的DiT视频生成模型不是很多,Open-Sora是开发者生态最好的一个,涵盖了DiT、时空DiT、3D VAE、Rectified Flow、因果卷积等Diffusion视频生成的经典知识点。本篇博客从Open-Sora的代码出发,深入解读背后的原理。

沉迷单车的追风少年 2264

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

#JavaCV 中 FFmpegFrameGrabber 使用时的一些注意事项

1、grabber.start()阻塞的解决方案         假设有如下代码,从Socket中读取视频流并显示。 FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(clientSocket.getInputStream()); grabber.setFormat("h264"); grabber.setOption("rtsp_transport", "tcp"); gr

投笔丶从戎的博客 2万+

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

java后台,视频截取某一帧图片并保存

FFmpegFrameGrabber ff = FFmpegFrameGrabber.createDefault("视频地址"); ff.start(); int ffLength = ff.getLengthInFrames(); Frame f; String pngPath = ""; int i = 0; while (i < ffLength) { f = ff.grab...

醉逸鎏年的博客 2804

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

ffmpeg java 播放视频_Javacv使用ffmpeg实现音视频同步播放

最近用javaCV的ffmpeg包的FFmpegFrameGrabber帧捕捉器对捕捉到的音频帧和视频帧做了同步的播放。采用的同步方法是视频向音频同步。具体的思路如下:(1)首先介绍ffmpeg是如何捕捉视频文件的图像和声音的FFmpegFrameGrabber fg = new FFmpegFrameGrabber("a video file path or a url);得到帧捕捉器对象后,调...

weixin_34652847的博客 1296

【java】JavaCV中FFmpegFrameGrabber调用start()方法时出现阻塞的解决办法

【java】JavaCV中FFmpegFrameGrabber调用start()方法时出现阻塞的解决办法

6734

FFmpeg+javacpp中FFmpegFrameGrabber

1、FFmpegFrameGrabber org\bytedeco\javacv\FFmpegFrameGrabber.java

许浑的博客 916

FFMPEG 参数详细说明

FFMPEG 3.4.1 版本参数详细说明 用法:ffmpeg [options] [[infile options] -i infile] … {[outfile options] outfile} …  获得帮助: -h - 打印基本选项 -h long - 打印更多选项 -h full - 打印所有选项(包括所有格式和编解码器特定选项,非常长) -h type = name - ...

程序手艺人 - 嵌入式音频技术之旅 7008

VLC播放器创建rtsp直播流、拉流操作,实现url在网页播放

VLC播放器作为服务器推流

moneyxjj的博客 8134

javacv和FFmpeg实现视频播放器的制作,和视音同步操作;

最近用javaCV的ffmpeg包的FFmpegFrameGrabber帧捕捉器对捕捉到的音频帧和视频帧做了同步的播放。采用的同步方法是视频向音频同步。具体的思路如下:(1)首先介绍ffmpeg是如何捕捉视频文件的图像和声音的[java] view plain copyFFmpegFrameGrabber fg = new FFmpegFrameGrabber("a video file path...

hongfei568718926的博客 3692

基于ffmpeg实现监控录屏,抓拍Java工具类

【代码】基于ffmpeg实现监控录屏,抓拍Java工具类。

weixin_52443523的博客 1455

Java获取视频封面图,利用FFmpegFrameGrabber获取视频封面图

【代码】Java获取视频封面图,利用FFmpegFrameGrabber获取视频封面图。

qq_42623400的博客 2488
上一篇: JavaCV 入门
下一篇: android ijkplayer 中遇到的问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值