基于javacv的视频编码格式判断及转码

org.bytedeco.javacv.FrameRecorder$Exception: av_write_frame() error -22 while writing video packet. FFmpeg推流报错org.bytedeco.javacv.FrameRecorder$Exception: av_write_frame() error -22 while writing video packet.问题解决 阅读详情

目标

将所有格式的视频 转码为mp4格式
升级版

依赖

<!-- 依赖很多,不需要的自行排除 -->
<!-- 转码功能只需要以ffmpeg、javacpp、javacv、openblas、opencv开头的jar包依赖 -->
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>javacv-platform</artifactId>
    <version>1.5.3</version>
</dependency>

从网上找的代码

咱没做过音视频开发,咱啥也不懂,只能从网上找代码,稍微加工一下

import java.io.File;

import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;

public class VideoUtils {
	
	public static String convert(File file) {
		FFmpegFrameGrabber frameGrabber = new FFmpegFrameGrabber(file.getAbsolutePath());
		String fileName = null;

		Frame captured_frame = null;

		FFmpegFrameRecorder recorder = null;
		
		try {
			frameGrabber.start();
			fileName = file.getAbsolutePath().replace(file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf(".")), "_recode.mp4");
			recorder = new FFmpegFrameRecorder(fileName, frameGrabber.getImageWidth(), frameGrabber.getImageHeight(), frameGrabber.getAudioChannels());
			recorder = new FFmpegFrameRecorder(fileName, frameGrabber.getImageWidth(), frameGrabber.getImageHeight(), frameGrabber.getAudioChannels());
			recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
			recorder.setFormat(format);
			recorder.setFrameRate(frameGrabber.getFrameRate());
			recorder.setVideoBitrate(frameGrabber.getVideoBitrate());
			recorder.setAspectRatio(frameGrabber.getAspectRatio());
			recorder.setAudioOptions(frameGrabber.getAudioOptions());
			recorder.setSampleRate(frameGrabber.getSampleRate());
			recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
			recorder.start();
			while (true) {
				try {
					captured_frame = frameGrabber.grabFrame();
					
					if (captured_frame == null) {
						System.out.println("!!! Failed cvQueryFrame");
						break;
					}
					recorder.setTimestamp(frameGrabber.getTimestamp());
					recorder.record(captured_frame);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			recorder.stop();
			recorder.release();
			frameGrabber.stop();
			frameGrabber.release();
			recorder.close();
			frameGrabber.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
//		file.delete();
		return fileName;
	}

	public static void main(String[] args) throws java.lang.Exception {
		String filePath = "D:/1590473414299_20200526.3gp";
		System.out.println("开始。。。");
        String convert = convert(new File(filePath));
        System.out.println(convert);
	}
	
}

遇到问题

以上代码转码大多数视频是正常的,但转码个别的视频报了以下错误

org.bytedeco.javacv.FrameRecorder$Exception: av_interleaved_write_frame() error -22 while writing interleaved video packet.
	at org.bytedeco.javacv.FFmpegFrameRecorder.writePacket(FFmpegFrameRecorder.java:1253)
	at org.bytedeco.javacv.FFmpegFrameRecorder.recordImage(FFmpegFrameRecorder.java:1048)
	at org.bytedeco.javacv.FFmpegFrameRecorder.record(FFmpegFrameRecorder.java:935)
	at org.bytedeco.javacv.FFmpegFrameRecorder.record(FFmpegFrameRecorder.java:928)
[libx264 @ 000000001b0d1980] non-strictly-monotonic PTS
[amrnb @ 000000001b0d4780] Multiple frames in a packet.
[libx264 @ 000000001b0d1980] non-strictly-monotonic PTS
[libx264 @ 000000001b0d1980] non-strictly-monotonic PTS
[mp4 @ 000000001d0f4040] Application provided invalid, non monotonically increasing dts to muxer in stream 0: 320937 >= 320937
[libx264 @ 000000001b0d1980] non-strictly-monotonic PTS
[mp4 @ 000000001d0f4040] Application provided invalid, non monotonically increasing dts to muxer in stream 0: 1604685 >= 1604685
[libx264 @ 000000001b0d1980] non-strictly-monotonic PTS
[mp4 @ 000000001d0f4040] Application provided invalid, non monotonically increasing dts to muxer in stream 0: 4814055 >= 4814055
[libx264 @ 000000001b0d1980] non-strictly-monotonic PTS
[mp4 @ 000000001d0f4040] Application provided invalid, non monotonically increasing dts to muxer in stream 0: 5562908 >= 5562908
[mp4 @ 000000001d0f4040] Application provided invalid, non monotonically increasing dts to muxer in stream 0: 8665299 >= 8665299
[aac @ 000000001d1004c0] 2 frames left in the queue on closing

这也太恶心了,开始从网上找解决方案,
有说是视频存在角度旋转的问题,但这例视频角度一直为0,
有说是dts和pts的问题,从异常信息中看应该是dts的问题,可是网上的解决方案都是C语言,咱也看不懂呀,就算看懂了,又能怎样,咱也不会用C语言呀。
只能尝试用java代码解决,javacv也没什么文档,网上的参考代码也只有那么一点。
看着网上的代码,改了一遍又改一遍,彻底恶心了,没有找到可解决问题的方案。

盯着屏幕上这几行代码

while (true) {
	try {
		captured_frame = frameGrabber.grabFrame();
		
		if (captured_frame == null) {
			System.out.println("!!! Failed cvQueryFrame");
			break;
		}
		recorder.setTimestamp(frameGrabber.getTimestamp());
		recorder.record(captured_frame);
	} catch (Exception e) {
		e.printStackTrace();
	}
}

实在无从下手呀

眼前的希望

除了异常大法,还有注释大法,哪里报错注哪里,so easy!!
于是我默默注释了一行代码

while (true) {
	try {
		captured_frame = frameGrabber.grabFrame();
		
		if (captured_frame == null) {
			System.out.println("!!! Failed cvQueryFrame");
			break;
		}
//		recorder.setTimestamp(frameGrabber.getTimestamp());
		recorder.record(captured_frame);
	} catch (Exception e) {
		e.printStackTrace();
	}
}

再次尝试,哈哈哈,竟然没报错,播放转码后的视频,也正常了。
不知道这个Timestamp是干什么用的,反正不用它就正常了。
更多格式的视频还没测试,先这样吧,附上最终代码

最终代码

import java.io.File;

import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;

public class VideoUtils {
	
	public static String convert(File file) {
		FFmpegFrameGrabber frameGrabber = new FFmpegFrameGrabber(file.getAbsolutePath());
		String fileName = null;

		Frame captured_frame = null;

		FFmpegFrameRecorder recorder = null;
		
		try {
			frameGrabber.start();
			fileName = file.getAbsolutePath().replace(file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf(".")), "_recode.mp4");
			recorder = new FFmpegFrameRecorder(fileName, frameGrabber.getImageWidth(), frameGrabber.getImageHeight(), frameGrabber.getAudioChannels());
			recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
			recorder.setFormat("mp4");
			recorder.setFrameRate(frameGrabber.getFrameRate());
			recorder.setVideoBitrate(frameGrabber.getVideoBitrate());
			recorder.setAudioBitrate(192000);
			recorder.setAudioOptions(frameGrabber.getAudioOptions());
			recorder.setAudioQuality(0);
			recorder.setSampleRate(44100);
			recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
			recorder.start();
			while (true) {
				try {
					captured_frame = frameGrabber.grabFrame();
					
					if (captured_frame == null) {
						System.out.println("!!! Failed cvQueryFrame");
						break;
					}
					recorder.record(captured_frame);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			recorder.stop();
			recorder.release();
			frameGrabber.stop();
			frameGrabber.release();
			recorder.close();
			frameGrabber.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
//		file.delete();
		return fileName;
	}

	public static void main(String[] args) throws java.lang.Exception {
		String filePath = "D:/1590473414299_20200526.3gp";
		System.out.println("开始。。。");
        String convert = convert(new File(filePath));
        System.out.println(convert);
	}
	
}

升级代码

package com.videotest;

import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class VideoUtils {
	private static final Logger logger = LoggerFactory.getLogger(VideoUtils.class);
	
	private static final String DEFAULT_FORMAT = "mp4";
	
	/**
	 * 判断视频编码是否H264并且音频编码是否ACC
	 * 现代浏览器对视频编码为H264并且音频编码为ACC并且封装格式为mp4格式的视频支持程度最好
	 * @param absolutePath 视频文件路径
	 * @return
	 * @throws Exception
	 */
	public static boolean isH264AndACC(String absolutePath) throws Exception {
		boolean isH264AndACC = false;
		String path = absolutePath;
		FFmpegFrameGrabber frameGrabber = FFmpegFrameGrabber.createDefault(path);
		try {
			frameGrabber.start();
			if (frameGrabber.getVideoCodec() == avcodec.AV_CODEC_ID_H264
					&& frameGrabber.getAudioCodec() == avcodec.AV_CODEC_ID_AAC) {
				isH264AndACC = true;
			}
		} finally {
			try {
				frameGrabber.close();
			} catch (org.bytedeco.javacv.FrameGrabber.Exception e) {
				logger.warn("frameGrabber.close异常", e);
			}
		}
		return isH264AndACC;
	}
	
	/**
	 * 将视频文件转码为视频编码为H264并且音频编码为ACC的mp4格式的视频
	 * @param absolutePath 视频文件路径
	 * @return
	 * @throws Exception
	 */
	public static String convert(String absolutePath) throws Exception {
		return convert(absolutePath, DEFAULT_FORMAT);
	}
	
	/**
	 * 将视频文件转码为视频编码为H264并且音频编码为ACC的视频
	 * @param absolutePath 视频文件路径
	 * @param format 转码后的封装格式
	 * @return
	 * @throws Exception
	 */
	public static String convert(String absolutePath, String format) throws Exception {
		String path = absolutePath;
		FFmpegFrameGrabber frameGrabber = FFmpegFrameGrabber.createDefault(path);
		String fileName = null;
		Frame captured_frame = null;
		FFmpegFrameRecorder recorder = null;
		
		try {
			int index = path.lastIndexOf(".");
			if (index < 0) {
				index += path.length();
			}
			fileName = path.replace(path.substring(index), "_recode." + format);
			frameGrabber.start();
			recorder = new FFmpegFrameRecorder(fileName, frameGrabber.getImageWidth(), frameGrabber.getImageHeight(), frameGrabber.getAudioChannels());
			recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
			recorder.setFormat(format);
			recorder.setFrameRate(frameGrabber.getFrameRate());
			recorder.setVideoBitrate(frameGrabber.getVideoBitrate());
			recorder.setAspectRatio(frameGrabber.getAspectRatio());
			recorder.setAudioBitrate(frameGrabber.getAudioBitrate());
			recorder.setAudioOptions(frameGrabber.getAudioOptions());
			recorder.setSampleRate(frameGrabber.getSampleRate());
			recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
			recorder.start();
			while (true) {
				captured_frame = frameGrabber.grabFrame();
				
				if (captured_frame == null) {
					logger.info(fileName + "转码完成");
					break;
				}
				recorder.record(captured_frame);
			}
		} finally {
			if (recorder != null) {
				try {
					recorder.close();
				} catch (Exception e) {
					logger.warn("recorder.close异常", e);
				}
			}
			try {
				frameGrabber.close();
			} catch (org.bytedeco.javacv.FrameGrabber.Exception e) {
				logger.warn("frameGrabber.close异常", e);
			}
		}
		return fileName;
	}
	
}

av_interleaved_write_frame(fmt_ctx, &enc_pkt);返回-22 av_interleaved_write_frame(fmt_ctx, &enc_pkt);返回-22, 原因之一: 各处寻找答案,都说是:pts和dts的问题。 我跟踪了pts和dts, 发现这两个值第一次是随机一个相等数,比如pts = dts = 486400. 然后第二次就是pts=dts = 487424, 可以看出,这是递增了1024. 所以,并不是pts和dts出了问题。毕竟官网的 音频编码和视频编码 的例子,最后也没有处理pts和dts。 原因之二: enc_pkt.s. 阅读详情

相关推荐

JavaCV异常:av_interleaved_write_frame() error -10053 while writing interleaved video packet

JavaCV异常:av_interleaved_write_frame() error -10053 while writing interleaved video packet问题描述解决方案 JavaCV异常汇总 JavaCV异常汇总 问题描述 推流的过程一般是:获取到每一个AVPacket或者AVFrame然后循环推送出去。 我出现这个报错的时候是在直播暂停时遇到。之前实现暂停功能的时候,是通过空跑for循环,不去获取pkt或者frame,而是让for循环空跑,当恢复播放时,再继续获取pkt或者f

banmajio的博客 5890

海康威视rtsp转rtmp(java稳定版)

1、测试的目标 将海康威视摄像头产生的rtsp视频流,通过java代码转换后,以rtmp的形式推送到nginx服务器,实现远程访问。推流程序需要和设备在同一个局域网下(否则获取不到视频流),nginx可以在本地服务器,也可以是云服务器。 2、工具和环境 eclipse、maven、jdk1.8、javacv-1.5.1、nginx1.17.1、VLC media player、iVMS-42...

yancie_的博客 1万+

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

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

qq_38334677的博客 6031

ffmpeg转封装报错 av_write_frame() error -22 while writing video packet.

packet= 1 fps= 25.0 dts= 21600 pts= 21600 duration= 3600, stream_index= 0 Error: [flv @ 0000000021dc8b80] Application provided invalid, non monotonically increasing dts to muxer in stream 0: 240 >=...

YuanWoSuiFeng的博客 6936

JavaCV 5.0-1.5.7 转码实战:解决 YouTube 上传失败的 3 个关键参数配置

本文详细介绍了使用JavaCV 5.0-1.5.7进行视频转码的关键参数配置,解决YouTube上传失败的常见问题。重点解析了H264编码、YUV420P像素格式和关键帧间隔设置等三个核心参数,帮助开发者生成符合YouTube标准的MP4文件,提升视频上传成功率。

weixin_33178839的博客 275

javaCV开发详解之18:音视频转码(音频编解码和视频编解码)

前言 在本章之前,我们已经分析了如何进行转流、转封装、像素格式转换以及音频的重采样,本章主要讲解和分析javaCV如何进行转码,其实很多小伙伴在转流章节中已经发现它已经在进行转码了,那么javaCV中的转码究竟是什么样的呢,本章就视频和音频转码进行剖析和讲解。 补充:与装封装和像素格式转换不同的是,编解码比转封装更深入一层,但是并不改变像素格式,想要改变像素格式,需要经过编解码的,也即是说编解码是介于装封装和像素格式转换之间的操作。 源码剖析 本章主要分析FFmpegFrameGrabber和FFmp

eguid音视频方向博客,分享经过实践检验的硬核音视频技术。FFmpeg、openCV和JavaCV音视频和图像处理系列教程发布十周年,众多开发者的严格验证。欢迎关注,一起交流探索学习最新音视频和图像处理技术 6300

JavaCV:将任意视频转码为h264编码的mp4格式视频

本文介绍了一个Java实现的视频转码工具类,使用FFmpeg进行视频格式转换。该工具类主要功能是将输入视频文件转码为指定格式的输出文件(推荐MP4格式)。核心方法通过FFmpegFrameGrabber获取视频信息(帧率、分辨率等),使用FFmpegFrameRecorder进行重新编码。代码包含完整的异常处理和资源释放机制,支持保留原始视频的编码参数。测试类展示了如何使用该工具进行MOV到MP4格式的转换。项目依赖JavaCV库,POM文件中配置了Linux平台下所需的GPU加速支持。

Alderaan的博客 1万+

JavaCV音视频开发宝典:视频转码和转封装有什么区别?使用rtsp拉流转推到rtmp案例来讲一下转码和转封装实现的区别

《JavaCV音视频开发宝典》专栏目录导航 《JavaCV音视频开发宝典》专栏介绍和目录 前言 博主本来想用mp4文件来讲得,但是手头没有合适的mp4小视频,所以就直接用rtsp转推rtmp的例子来讲,一来rtsp转rtmp比较常见,而且还方便理解概念。 已知rtsp可以支持h264、h265视频编码,而rtmp只支持h264编码视频 转码方式 假设1:现在我们已有一个h265编码的rtsp视频源(称作:rtsp://A),如果要转推到rtmp流媒体服务(称作:rtmp://B),则必须要转码成h.

eguid音视频方向博客,分享经过实践检验的硬核音视频技术。FFmpeg、openCV和JavaCV音视频和图像处理系列教程发布十周年,众多开发者的严格验证。欢迎关注,一起交流探索学习最新音视频和图像处理技术 2690

基于javacv的视频转码(升级版)

目录目标依赖VideoFormat.javaVideo.java怎么使用 目标 将所有格式的视频 转码为mp4格式 依赖 <!-- 依赖很多,不需要的自行排除 --> <!-- 转码功能只需要以ffmpeg、javacpp、javacv、openblas、opencv开头的jar包依赖 --> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-

daodfs1的博客 2939

JavaCV音视频开发宝典:vb8和vp9编码的webm格式视频文件转成mp4文件

​mp4不用多说,我们日常生活中,手机上或者浏览器网页中使用最多的视频文件就是mp4了。 除了mp4之外,现代浏览器(主要是Chrome、Firefox、Safari、Edge)对webm格式的视频支持也非常好(如下图所示),因此有越来越多的视频网站开始使用webm格式来作为主要的视频格式。 但是除了新的空气显卡(明指老黄rtx3000系列)外,老的显卡一般时不支持gpu硬解webm的视频的,所以为了便于观看和存储,我们一般需要将webm再转成mp4视频文件。...

eguid音视频方向博客,分享经过实践检验的硬核音视频技术。FFmpeg、openCV和JavaCV音视频和图像处理系列教程发布十周年,众多开发者的严格验证。欢迎关注,一起交流探索学习最新音视频和图像处理技术 1868

javaCV开发详解之8:转封装在rtsp转rtmp流中的应用(无须转码,更低的资源消耗)

javaCV系列文章: javacv开发详解之1:调用本机摄像头视频 javaCV开发详解之2:推流器实现,推本地摄像头视频到流媒体服务器以及摄像头录制视频功能实现(基于javaCV-FFMPEG、javaCV-openCV) javaCV开发详解之3:收流器实现,录制流媒体服务器的rtsp/rtmp视频文件(基于javaCV-FFMPEG) javaCV开发详解之4:转流器实现(也可作...

eguid音视频方向博客,分享经过实践检验的硬核音视频技术。FFmpeg、openCV和JavaCV音视频和图像处理系列教程发布十周年,众多开发者的严格验证。欢迎关注,一起交流探索学习最新音视频和图像处理技术 2万+

JavaCV视频转码实战:格式转换与压缩优化技巧

你是否曾遇到过以下问题: - 录制的4K视频体积过大,无法分享到社交媒体? - 下载的视频格式在设备上无法播放? - 转码后的视频画质模糊或音频不同步? 本文将通过JavaCV(Java Computer Vision)库,提供一套完整的视频转码解决方案,帮助你轻松实现格式转换与压缩优化。读完本文后,你将能够: - 使用JavaCV进行视频格式转换 - 优化视频压缩参数以减小文件体积 - 处理转...

gitblog_00727的博客 1000

JavaCV音视频开发宝典:无需流媒体服务也无需转码,使用JavaCV和springBoot实现http-flv转封装直播服务,浏览器网页flv.js直接播放rtp、rtsp、rtmp实时视频

本章代码除了在《JavaCV音视频开发宝典:使用JavaCV和springBoot实现http-flv直播服务,无需流媒体服务,浏览器网页flv.js转封装方式播放rtsp,rtmp和桌面投屏实时画面》基础上增加转封装功能,同样也适用于websocket-flv实现《JavaCV音视频开发宝典:使用JavaCV和springBoot实现websocket-flv直播服务,无需流媒体服务,浏览器网页flv.js播放器直接播放rtsp,rtmp等实时视频》中。这是个老话重谈的问题了。

eguid音视频方向博客,分享经过实践检验的硬核音视频技术。FFmpeg、openCV和JavaCV音视频和图像处理系列教程发布十周年,众多开发者的严格验证。欢迎关注,一起交流探索学习最新音视频和图像处理技术 3404

微信打不开MP4文件 (记录编码问题)

微信打不开MP4文件,无法分享,qq可以打开

一个后端菜鸟的博客 8595

JavaCV入门指南:帧录制器/推流器(FrameRecorder)的原理与应用

JavaCV入门指南系列: JavaCV入门指南:序章 JavaCV入门指南:调用FFmpeg原生API和JavaCV是如何封装了FFmpeg的音视频操作 JavaCV入门指南:帧抓取器(FrameGrabber)的原理与应用 JavaCV入门指南:帧录制器/推流器(FrameRecorder)的原理与应用 目录 前言 FrameRecorder(录制器/推流器)介绍 FrameRecorder的结构和分析 两个FrameRecorder实现类的介绍 FrameRecorder的结构.

eguid音视频方向博客,分享经过实践检验的硬核音视频技术。FFmpeg、openCV和JavaCV音视频和图像处理系列教程发布十周年,众多开发者的严格验证。欢迎关注,一起交流探索学习最新音视频和图像处理技术 1万+

JavaCV视频截图工具开发实践

在当今数字化时代,多媒体内容处理变得至关重要,而JavaCV正是为了解决Java语言在多媒体处理领域中遇到的问题而生。JavaCV是一个开源项目,它将FFmpeg和OpenCV等强大的多媒体处理库,封装成易于使用和理解的Java接口。这一封装大大降低了Java程序员对底层复杂性的理解负担,使得他们能够专注于业务逻辑的实现。JavaCV的核心功能涉及视频和音频的编解码、帧的捕获和处理、图像的转换和分析等,它主要面向的是那些希望在Java环境中利用现有的多媒体处理能力的开发者。

weixin_36299472的博客 787

别再硬肝了!Java带你轻松玩转视频转码

这篇文章介绍了Java视频转码技术的原理与应用。首先阐述了视频的本质是由连续图像和音频组成,不同格式(如MP4、AVI)采用不同编码方式(如H.264)进行压缩。转码的核心是将视频解码后重新编码为目标格式,主要类型包括信源格式转换、降码率和降分辨率转码。文章还详细比较了四种提升转码效率的方案:像素域闭环、开环、频率域闭环和快速频率域闭环方案,分析各自的优缺点。通过Java实现视频转码,可以解决格式兼容性问题,适应不同播放需求。

m0_73590302的博客 1402

java视频转码

该资源可以帮助你解决视频之间的转码问题,轻松实现视频格式之间的转换

上一篇: mybatis注解动态sql中foreach工具方法
下一篇: 记一次线上BUG - pdf转图片
daodfs1
博客等级 码龄9年 45粉丝 81原创
评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值