编写DirectShow Filters—Filters如何连接

DirectShow应用:用程序连接Filters实现媒体播放 DirectShow应用:用程序连接Filters实现媒体播放   Windows平台下的音视频应用,多以DirectShow为开发工具,通过Graphedit选择合适的Filters并连接成实际的链路,就可以实现简单播放。但在程序中如何实现呢,笔者带着这个问题进行了探索,现将初步成果呈现出来,以期收抛砖引玉之效,共同提高。 一.       枚举Filters Windows系统中的Fi 阅读详情
1. PIN连接
filters通过IPin接口连接pin。output pin连接到input pin。每个pin连接有一个media sample,通过am_media_type描述。
一个应用程序通过调用在filter graph manager上的方法来连接filter,而不是通过filters或者pins上的方法。应用程序可以直接指定哪些filters连接,通过IFilterGraph::ConnectDirct或者IGraphBuilder::Connect。或者使用如IGraphBuilder::RenderFile的graph-building方法间接连接filters。
如果连接成功,两个 filter必须在filter graph中,应用程序可以通过IFilterGraph::AddFilter方法增加一个filter到graph中,filter graph manager可以增加filter到graph。当一个filter被增加到graph中,filter graph manager 调用filter的IBaseFilter::JoinFilterGraph来通知这个filter。
一般连接过程的概述如下:
1) Filter graph manager调用在output pin上Ipin::Connect,传递一个指针到input pin。
2) 如果output pin接受这个连接,它调用在input pin上的Ipin::ReceiveConnection。
3) 如果output pin也接受这个连接,连接成功并且pins连接。
一些pin可以在filter激活时断开和重新连接。这种连接类型叫dynamic reconnection。大多数filter不支持动态重连
filter一般用downstream顺序连接,换句话,filter的input pin等于output pin连接,filter应该一直支持这种连接顺序。也有filter连接顺序相反—output pin先连接,再连接input pins。如,可能先连接一个MUX filter的output pin到file-writer filter,再连接mux filter的input pin。
当pin的 connect或者ReceiveConnection方法被调用时,这个pin必须验证是否支持这个连接,细节依赖于具体的filter,大多数步骤如下:
1) 查检media type是否可接受
2) 协商一个allocator
3) 查询被请求接口的其它pin
2. 协商media type
Filter graph manager调用Ipin::connect方法时,有几个选择来指定media type
1) complete type : 如果media type全指定,pins试图用这种类型连接。如果不能连接,连接失败。
2) Partial media type :如果主类型、子类型、格式类型是GUID_NULL,media type是partial,GUID_NULL如同通配符,表示可以接受任何值。这个PIN协商于兼容于partial类型的类型。
3) No media type : 如果filter graph manager传递一个NULL指针,两个PINS允许接受任何media type。
如果pin连接了,连接会一直有一个完整的media type,它的目标是让filter graph manager限制可能的连接类型
在协商过程中,output pin通过调用input pin的Ipin::ReceiveConnection来倾向一个media type,这个input pin可能接受或拒绝这个类型,这个过程重复直至有一个input pin接受这个连接,或者output pin尝试所有所有连接然后连接失败。
一个output pin如何选择media type依赖于它的实现。在directshow基类中,output pin调用在input pin上Ipin::EnumMediaTypes,它返回一个枚举器枚举input pin倾向的media type,如果失败,output pin枚举自己倾向的类型
用media type工作
任何函数接受AM_MEDIA_TYPE参数,在引用pbFormat成员前要一直验证cbFormat和formattype值。如下代码是错误的:
if(pmt->formattype==FORMAT_VideoInfo)
{
 VIDEOINFOHEADER *pVIH = (VIDEOINFOHEADER*)pmt->pbFormat;
 //wrong
}
正确的代码是:
if((pmt->formattype==FORMAT_VideoInfo) &&
   (pmt->cbFormat>sizeof(VIDEOINFOHEADER)&&
   (pbFormat!=NULL)
{
 VIDEOINFOHEADER *pVIH = (VIDEOINFOHEADER*)pmt->pbFormat;
 //now you can dereference pVIH
}
3. 协商allocator
当两个pin连接,需要一个交换media数据的机制,它叫做transport,一般,directshow体系关于transport是不确定的。两个filter可以允许使用任何两个都支持transport来连接。
大多数transport是local memory transport,即 media数据保留在主内存中。存在两个flavor本地内存transport,如push model / pull model,在push模型中,source filter push数据到downstream filter,使用在downstream filter的input pin上的IMemInputPin接口 ,在pull模型中,downstream filter从source filter请求数据,使用在source filter output pin上的IAsyncReader接口
在local memory transport,对象响应分配memory buffer被叫做allocator,一个allocator支持IMemAllocator接口,两个PINs共享一个单独的allocator,任何一个pin可以提供一个allocator,但output pin选择哪个allocator来使用
Output pin也设置allocator的属性,属性决定有allocator建立多少个buffers,每个buffer的尺寸,和内存对齐,output pin服从于input pin 的buffer请求。
在ImemInputput连接中,allocator协商工作如下:
1) 可选的,output pin调用IMemInputPin::GetAllocatorRequirements,它返回input pin的buffer请求,如内存对齐。一般的,output pin应该尊重于 input pin的请求,除非有好的原因不去这么做。
2) 可选的,output pin调用ImemInputPin::GetAllocator,它从input pin请求一个allocator,input pin提供一个或者返回错误代码。
3) 输出pin选择一个allocator,它能使用input pin提供的一个,或者自己创建。
4) Output pin调用IMemAllocator::SetProperties设置allocator properties属性。可是,allocator可能不理会被请求的属性(如,如果input pin提供allocator时这种情况可能发生),在SetProperties方法中allocator返回实际的属性作为output参数。
5) outpin调用ImemInputPin::NotifyAllocator通知input pin的选择
6) Input pin应该调用ImemAllocator::GetProperties验证是否allocator属性可接受的。
7) Output pin响应提供或反提交allocator,这个发生在流开始或停止时。
在一个IAsyncReader连接中,allocator协商工作如下:
1) input pin调用在output pin上的IAsyncReader::RequestAllocaor,这个input pin指定它的buffer请求,可选的,提供一个allcator。
2) Output pin选择一个allocator,它可能使用input pin提供的一个,或者自己建立。
3) Output pin返回allocator作为在RequestAllocator中的outgoing参数,input pin应该查检allocator属性。
4) Input pin响应于提交或反提交allocator
5) 在allocator协商过程中任何时间,任何pin可能连接失败。
6) 如果output pin使用input pin的allocator,它可能仅使用这个allocator传递sample到输入pin,所属的filter必须不使用allocator来传递sample到其它的pins。
4. 提供一个自定义的allcator
本节描述如何为一个filter提供一个自定义allocator。只描述了IMemInputPin连接,但IAsyncReader连接步骤是类似的。
首先,为allocator定义一个C++类。自定义的allocator继承自一个标准的allocator类,CBaseAllocator或者CMemAllocator,或者你可以建立全新的allocator类,如果这样,必须暴露ImemAllocator接口。
保留的步骤依赖于在自定义filter上的allocator是否属于一个input pin或者一个output pin,在allocator协商分析期间input pin扮演一个不同于output pin的角色,因为output pin无条件选择这个allocator。
1) 为input pin提供一个自定义allocator
为了为一个input pin提供一个allocator,重载input pin上的CBaseInputPin::GetAllocator。在这个方法中,查检m_pAllocator成员变量,如果它为非空,意味着allocator已经被选为这个连接,所以GetAllocator必须返回一个指向allocator的指针。如果为NULL,意味着allocator没被选择,所以GetAllocator返回一个指针指向input pin倾向的allocator,在这个情况下,建立一个自定义的Allocator的实例并且返回它的IMemAllocator指针。下列代码显示如何实现GetAllocator方法:
STDMETHODIMP CMyInputPin::GetAllocator(IMemAllocator **ppAllocator)
{
 CheckPointer(ppAllocator, E_POINTER);
 if(m_pAllocator)
 {
  //we already have an allocator, so return that one
  *ppAllocator = m_pAllocator;
  (*ppAllocator)->AddRef();
  return S_OK;
 }
 // no allocator yet, so propose our custom allocator, the exact coe here will depend on
 // your custom allocator class defininition
 HRESULT hr = S_OK;
 CMyAllocator * pAlloc = new CMyAllocator(&hr);
 if(!pAlloc)
 {
  return E_OUTOFMEMORY;
 }
 if(FAILED(hr))
 {
  delete pAlloc;
  return hr;
 }
 //return the IMemAllocator interface to the caller
 return pAlloc->QueryInterface(IID_IMemAllocator, (void**)ppAllocator);
}
当upstream filter选择一个allocator,它调用input pin的IMemInputPin::NotifyAllocator方法。重载CBaseInputPin::notifyAllocator方法来查检allocator属性,在一些情况下,input pin可能拒绝这个allocator如果不是你自定义的allocator,虽然可能引起所有pin连接失败。
2) 为output pin提供一个自定义的allocator
为了为output pin提供一个allocator,重载CBaseOutputPin::InitAllocator建立自定义的allocator的一个实例:
HRESULT MyOutputPin::InitAllocator(IMemAllocator **pAlloc)
{
 HRESULT hr = S_OK;
 CMyAllocator * pAlloc = new CMyAllocator(&hr);
 if(!pAlloc)
 {
  return E_OUTOFMEMORY;
 }
 if(FAILED(hr))
 {
  delete pAlloc;
  return hr;
 }
 //return the IMemAllocator interface
 return pAlloc->QueryInterface(IID_IMemAllocator, (void**)ppAllocator);
}
默认的,CBaseOutpin类首先从input pin请求一个allocator,如果allocator不合适,output 建立它自己的allocator,为了强制连接使用你自定义的allocator,重载CBaseOutputPin::DecideAllocator,可是,要意识到它可能阻止你的output pin连接到确定的filter,因为其它fitlers也许请求自己所属的自定义allocator。
5. 重新连接pin
在一个pin连接期间,一个filter可能断开连接和再连接一个或多个pin,如下
1) filter调用在另一个filter pin的Ipin::QueryAccept,并且指定新的媒体类型。
2) 如果queryAccept返回S_OK,FILTER调用IFilterGraph2::ReconnectEx重新连接此PIN。
下列例子需要可能重新连接pins:
1) Tee filter:tee filter分离input stream为多个输出流而不改变流中数据。tee fitler可能接受一定范围的media type,但这些type必须匹配贯穿所有pin连接。因此,当input pin连接时,filter可能需要重新协商在output pin上的任何存在的连接。如InfTee Filter sample。
2) Trans-in-place filter : Trans-in-place filter修改在原始buffer的input数据代替拷贝数据到一个单独的out buffer,对于upstream / downstream 连接,它必须使用同样的allcaotor,第一个pin连接(input / output ) 用一般方式中协商一个allcator,当其它的pin连接,可是,第一个allcoator可能不被接受,在这个情况下,第二个pin选择一个不同的allocator,第一个pin重新连接使用新的allocator。例如,在CTransInPlaceFilter类
在ReconnectEx中,filter graph manager异步断开连接和重新连接pin,filter必须不会试图重新连接除非QueryAccept返回S_OK,另外,PIN将被断开连接,引起graph错误,filter也从Ipin::Connect内部请求重新连接,在同样的线程中,如果连接返回一个线程,当另一个线程有重新连接的请求,filter graph manager可能在它重新连接之前run,引起graph错误。
DirectShow之filters例程详解前言 direcshow filter里程的解读和分析。要开发filter,研读directshow提供的filter例程是最好的方法,该文是对filter的一个简单介绍,随后会对每一个filter例程进行详细的解读 阅读详情

相关推荐

Directshow播放框架详解

windows播放框架有directshow,ffmpeg等,本篇主要介绍direcshow,对应quartz.dll 接口: IFilterGraph 过滤通道接口 IFilterGraph2 增强的IFilterGraph IGraphBuilder 最为重用的COM接口,用于手动或者自动构造过滤通道Filter Graph Manager IMediaControl 用来控制流媒体,例如流的启动和停止暂停等,播放控制接口 IMediaEvent 播放事件接口 ,该接口在Filter Graph发

wangbuji的博客 1270

DirectX:函数可以连接任意两个filter 分类: Direct...

函数可以连接任意两个filterHRESULT ConnectFilters( IBaseFilter *pSrc, IBaseFilter *pDest ) { IPin *pIn = 0; IPin *pOut = 0; IEnumPins *pOutEnum; IEnumPins *pInEnum; PIN_DIRECTION ThisPinDir; HRESU...

weixin_30606461的博客 153

西门子S7-1200 PLC与伺服电机控制实战解析

工业自动化控制系统中,PLC(可编程逻辑控制器)与伺服电机的协同工作是实现精密运动控制的核心技术。通过PROFINET工业以太网协议,PLC可以高效地控制多台伺服电机,实现定位、同步和插补等复杂运动。在实际工程应用中,硬件组态的正确配置和运动控制算法的优化是关键挑战。本文以西门子S7-1200 PLC控制五台伺服电机的项目为例,详细介绍了从硬件组态、运动控制程序实现到HMI界面设计的全流程。特别针对多轴同步插补运动、动态速度曲线调整等特殊工艺需求,提供了实用的解决方案和调试技巧,为工业自动化领域的工程师提供

weixin_30252709的博客 497

DirectShow开发快速入门之二数据流的流动

Directshow数据流动概述  Filter之间的数据是通过Sample来传送的。Sample是一个COM组件,拥有自己的一段数据缓冲buffer,这个com组件暴露了IMediaSample接口。这个sample一般都有一个叫做内存分配器(alloctor)的com对象来创建管理,这个对象具有IMemAllocator接口。如下图所示:

李逸波(PMP) 1551

静态随机存储器(SRAM)

静态随机存储器(SRAM)是一种广泛用于计算机系统和嵌入式设备中的高速随机访问存储器。与动态随机存储器(DRAM)不同,SRAM 不需要定期刷新,因此可以实现更快的访问时间和更低的功耗。SRAM通常用于缓存、高速缓冲器和存储器应用程序中,其中性能和可靠性至关重要。

JAZJD的博客 8148

verilog学习——Verilog的行为建模/Behavioral modeling(2)

函数的用途是返回要在表达式中使用的值。函数定义始终以关键字function开始,后跟返回类型、名称和在参数中的端口列表。函数以关键字endfunction结束。函数至少有一个输入,如果不返回任何内容,则返回类型为void。由两种方法可以声明函数的输入;函数定义隐式地创建一个与函数同名的内部变量。通过将函数结果分配给内部变量来初始化返回值。函数调用是带有表达式的操作数。① 函数不能包含任何时间控制语句,如#、@、wait、posedge、negedge;

LXY241638的博客 1360

DirectShow体系结构之Pin

   Filter加入Filter Graph后,由Filter Graph对其进行控制,完成需要的操作。但是Filter之间并不是直接相连的。Filter只完成对数据的处理,在Filter Graph中数据的接收和输出并不是完全由Filter自身完成的。这里需要介绍另一个COM组件—Pin。Pin分为输入Pin和输出Pin两种,分别完成数据的输入和输出工作,它是Filter处理数据的来源和去向。...

冷月醉雪的博客 817

企业微信第三方应用014_企业微信5种第三方应用的安装方式_上架到应用市场_服务商后台点击安装测试_生成应用推广二维码安装---企业微信开发016

成员授权的意思就是,只要是这个成员扫码安装了这个应用,那么这个应用就只有这个成员可以用。安装的时候可以设置可见范围,这个地方可以设置整个公司或者是某个部门或者是某些人可以让这个应用来进行操作。创建了以后,这个二维码就可以发出来进行推广别人用户扫描了以后就可以把自己的企业微信给安装上对应的应用了。然后在对应的这个微信的企业后台就可以看到在第三方中有这个应用了。可以看到这个就是服务商的后台对应的一个应用,我们点击打开应用。可以看到这里有两个授权,一个是成员授权,一个是管理员授权。就可以去创建推广二维码了。

添柴程序猿的专栏 589

Excel公式中查找满足行列条件数据的三种解法

1. 灵活使用vlookup, index+match, filter函数可以实现多条件的一键查找功能。2. 以上方法,通过wps验证成功。2019版Excel可能不支持filter函数,建议大家使用wps来测试。3. 补充:利用双match法做下面的题:双match法做题。

henanlion的博客 8784

零基础DSP实战TMS320F28035:第九节,ADC比较器&EPWM

ADC比较器框图如下所示:-电压-----------------------------输出-电压A>电压B------------------ 1-电压A

sinat_30943509的博客 2535

第六节 LLaVA模型数据处理源码解读(input_ids/labels/attention_mask/image,上篇)

本篇文章直接介绍llava模型数据加工部分,整体结构说明llava多模态模型输入数据格式,其中包含input_ids/labels/attention_mask与image格式,并给出对应代码位置与整个数据加工流程。最重要,通过debug给出数据解释与代码解读,也通过一个真实数据给出模型输入格式说明。当你阅读完此篇文章,绝对透彻理解llava数据加工过程与内容。显然,一篇博客是无法呈现整个数据细节,我将继续下篇博客呈现数据细节处理。result = input.ne(3) # torch.ne函数调用。

weixin_38252409的博客 3193

Java Stream中filter如何串联多个条件?90%的开发者都忽略了这一点

掌握Java Stream流filter多个条件的正确串联方法,避免逻辑错误与性能隐患。适用于集合筛选等场景,通过Predicate组合实现灵活过滤,提升代码可读性与维护性,90%开发者忽略的关键技巧,值得收藏。

InitFlow的博客 474

Docker 环境 Playwright MCP 部署全流程指南

Docker部署Playwright MCP全流程指南 本文详细介绍了使用Docker容器化部署Playwright MCP的完整流程,涵盖镜像拉取、容器启动、配置优化和问题排查。主要内容包括: 核心部署步骤:通过官方镜像快速搭建环境,支持自定义端口和数据持久化 关键配置要点:详解网络设置、持久化目录挂载和环境变量配置 服务验证方法:通过日志检查和服务连通性测试确保部署成功 客户端集成:提供VS Code等IDE的配置示例 常见问题解决:针对浏览器启动超时、跨域限制等问题给出解决方案 实战案例:演示自动化爬

勤奋的码农 1473

编写DirectShow Filters—DirectShow and COM

seeker一、 如何实现IUnknown Microsoft DirectShow基于组件对象模型(COM)。如果你编写自定义filter,你必须把它作为一个COM对象来实现。DirectShow基类提供一个实现此对象的框架。不过使用基类不是必须的,但它能简化开发进度。这篇文章描述一些COM对象的内部实现细节和它们在DirectShow基类的实现。本文假定你知道如何编程COM客户端程序—

CP9的专栏 3673

【人工智能】OpenClaw网关API速率限制报错全解析 API rate limit reached. Please try again later.

对应HTTP标准状态码为429(请求过多)。该报错集中出现在OpenClaw GATEWAY DASHBOARD的聊天会话界面,核心特征为:用户最初以为是单一模型服务商的问题,先后更换OpenAI、Gemini、Ollama、Moonshot、Mercury-2等多个主流大模型API后,该报错仍稳定触发,尤其在执行复杂任务时高频出现。

奶爸的编程之路,也就一周冷个三天 6459

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

编写DirectShow Filters—编写transform filters

seeker本节描述如何编写一个transform filter,这种filter一般要有一个输入PIN和一个输出PIN。为了演示这些步骤,本节描述一个假定的transform filter,它输出run-length编码(RLE)视频。并不描述RLE-编码算法本身。(DirectShow已经通过avi compressor filter提供一个RLE codec。)本章假定你将使用DirectS

CP9的专栏 7126

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

编写DirectShow Filters—动态格式改变

当两个filter 连接时,他们在一种媒体类型达成一致。这种媒体类型描述upstream filter将传递数据的格式。在大多数情况下,在连接的持续过程中,这个媒体类型是不变的,但是,directshow 也提供限制支持filter改变媒体类型。当一个filter转换媒体类型,叫做dynamic format change。如果你编写一个filter graph,应该意识到动态格式改变的机制。即使

CP9的专栏 5966

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

编写DirectShow Filters—概述

seeker(一) 介绍DirectShow Filter开发本章提供开发一个自定义DirectShow filter的简单框架描述,也提供了链接到更详细讨论这些任务的主题。在阅读本章之前,阅读在About DirectShow主题,它描述了全面的DirectShow构架。1. DirectShow基类库Directshow sdk包括了编写filters的一组C++类。虽然它们不是必需的,但

CP9的专栏 3389

编写DirectShow Filters—质量控制管理

质量控制是一种通过filter graph调整数据速率来响应运行性能的机制。如果renderer filter接收太多数据或太少数据,它会发送一个质量消息。这个质量消息请求数据速率的调节。默认的,质量消息从renderer向上直到达到能响应它(如果有)的filter。应用程序也可以实现一个自定义的质量管理器。如果是这种情况,renderer直接传递质量消息到应用程序的质量管理器。一、 质量消息质量

CP9的专栏 3059

编写DirectShow Filters—线程和关键区

seeker本章描述了dshow filters 的线程,这些步骤可以让你在自定义filter 中避免系统崩溃和死锁。在本章中的例子使用伪码为演示你需要去编写的代码。假定自定义filter使用从Directshow基类继承的类。1. CMyInputPin::继承自CBaseInputPin2. CMyOutputPin: 继承自CBaseOutputPin3. CMyFilter:继承自CB

CP9的专栏 5698

【免费下载】 探索LAV Filters:全能的DirectShow解码器

LAV Filters 是一组基于FFmpeg项目的libavformat和libavcodec库的DirectShow过滤器。这些过滤器能够让你在DirectShow播放器中播放几乎任何格式的媒体文件。尽管项目仍在开发中,并非所有功能都已完成,也并非所有格式都支持,但它已经能够处理包括MKV/WebM、AVI、MP4/MOV、TS/M2TS/MPG、FLV、OGG等多种格式。 ## 项目技术分

gitblog_00507的博客 1788

LAV Filters:开源DirectShow媒体分离与解码器全面解析

LAV Filters:开源DirectShow媒体分离与解码器全面解析 LAV Filters是一套基于FFmpeg项目的开源DirectShow媒体分离器与解码器集合,由Hendrik Leppkes开发维护。该项目诞生于2010年,旨在为Windows平台提供高质量、开源的多媒体处理解决方案,填补了当时Windows平台上缺乏统一高效DirectShow过滤器解决方案的空白。LAV Filt...

gitblog_01041的博客 1433

LAV Filters配置教程:DirectShow媒体解码器终极指南

LAV Filters是一套开源的DirectShow媒体分离器和解码器,能够为Windows系统提供强大的音视频解码能力。本指南将帮助你快速掌握LAV Filters的安装与配置方法,让你的媒体播放体验提升到新高度。 ## 一、LAV Filters简介 LAV Filters包含三个核心组件: - **LAV Splitter**:负责解析各种媒体容器格式 - **LAV Audio**:

gitblog_00719的博客 679

LAV Filters

lav filters是一组基于ffmpeg项目中的libavformat/libavcodec库的directshow分离器和音视频解码器,功能强大,可以支持用户在directshow播放器中播放任何格式的媒体文件。基本语法很简单,它总是需要一对音频和字幕语言,用冒号分隔,例如:“eng:ger”此外,将始终使用标记为“默认”或“强制”的字幕。默认模式将选择符合您语言偏好的字幕。如果音频是英文,则加载英文或德文强制字幕轨道,否则关闭字幕。音频流选择提供了一些灵活性,特别是您可以配置您的首选语言。

YANshangqian的博客 933

编写DirectShow Filters—Filter的数据流动

seeker本节详细描述数据如何通过filter graph流动。集中于使用IMemInputPin/IAsyncReader的本地内存传输使用。为编写自定义filter的开发者提供了扩展。大量数据通过一个filter graph移动。粗略可为两类:媒体数据和控制数据。一般来说,媒体数据按downstream顺序传递而控制数据按upstream顺序传递。媒体包括视频帧、音频采样、MPEG包等等来形

CP9的专栏 4888
上一篇: 编写DirectShow Filters—概述
下一篇: 编写DirectShow Filters—Filter的数据流动
mapper
博客等级 码龄19年 31粉丝 24原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值