ffmpeg视频编解码 demo初探(一)(包含下载指定windows版本ffmpeg)分离视频文件中的视频流每一帧YUV图片

news2025/7/11 14:59:41

参考文章1:YUV数据流编码成H264

参考文章2:【FFmpeg编码实战】(1)将YUV420P图片集编码成H264视频文件

文章目录

    • 第一个项目:分离视频文件中的视频流每一张图片
      • 弯路
      • 步入正轨
        • 下载官方编译的ffmpeg4.3(win64-gpl-shared-4.3.zip)
        • 拷贝ffmpeg文件到项目
        • 配置vs
        • 运行项目
        • 查看文件

第一个项目:分离视频文件中的视频流每一张图片

先参考这个文章:【FFmpeg解码实战】(2)分离视频文件中的视频流每一张图片(进阶)(C)

这篇文章的上一篇文章:【FFmpeg解码实战】(1)解码并分离视频文件中的音频流和视频流(C),也是第一篇demp,我们跳过

博主提供了工程下载方式:VS2019-解码视频-工程所有文件.zip

这个下载的项目其实是把解码并分离视频文件中的音频流和视频流分离视频文件中的视频流每一张图片合在一起了,通过宏开关去控制
在这里插入图片描述

弯路

里面没有ffmpeg,我们下载博主编译好的或者直接到官网下载

博主给的:

你要ARM 的话,可以使用这个: https://download.csdn.net/download/Ciellee/13027058
windows 的话,可以用这个 : https://download.csdn.net/download/Ciellee/12915075
都是我之前用最新的 4.3 源码自已编译的

我没有用博主的,直接找方法自己编译了一个,不过是4.4版本的,不知道兼容不兼容
Visual Studio 2019 VS编译ffmpeg4.4为静态库或动态库

然后我们在博主的工程里面,添加include和lib两个目录,并且把需要的头文件和库文件拷贝进去

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在vs项目种配置附加包含目录和附加库目录

看vs项目的这个地方发现博主是这样包含ffmpeg的头文件和库目录的,我们把它改了
在这里插入图片描述
在这里插入图片描述

改成这样
在这里插入图片描述
在这里插入图片描述
然后运行项目,发现报错了,错误 C4996 'av_init_packet': 被声明为已否决,应该这个函数不推荐使用了,我们看下4.4有没替代接口
在这里插入图片描述

在ffmpeg编译工程里查找,发现这个文档,参考文章:ffmpeg的API函数变化记录

在这里插入图片描述

看了下,貌似没替代接口,那么我们只能“沉默”它了
在这里插入图片描述
在代码开头添加#pragma warning(disable : 4996)
在这里插入图片描述
运行报错:
在这里插入图片描述
按博主提供的ffmpeg4.3配置了,也还是同样问题

严重性 代码 说明 项目 文件 行 禁止显示状态
警告 MSB8028 中间目录(x64\Debug)包含从另一个项目(1-解码视频.vcxproj)共享的文件。
这会导致错误的清除和重新生成行为。 demux_video C:\Program Files (x86)\Microsoft Visual
Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppBuild.targets 513

严重性 代码 说明 项目 文件 行 禁止显示状态
警告 LNK4078 找到多个“.rdata”节,它们具有不同的特性(C0400040) demux_video F:\ArnoldCppTest\20221113_ffmpeg_test\prj\libavformatd.lib(invert_limb_table.obj) 1

步入正轨

下载官方编译的ffmpeg4.3(win64-gpl-shared-4.3.zip)

遇到很多困难啊,后来发现是博主给的ffmpeg有问题,后来我到ffmpeg官网,图中箭头所指的地方下到了4.3老版本

在这里插入图片描述

我一直往后面翻,终于找到了4.3老版本,

https://github.com/BtbN/FFmpeg-Builds/releases

然后下载了

https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2021-03-31-12-39/ffmpeg-n4.3.2-162-g4bbcaf7559-win64-gpl-shared-4.3.zip

在这里插入图片描述
然后下下来解压,把里面的一些东西拷贝到我们项目目录中,主要就是头文件,静态库文件,动态库文件,其中动态库文件注意要放在项目根目录

拷贝ffmpeg文件到项目

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

配置vs

因为下下来的没见带d,所以估计是release版本的,我们在Release x64下配置

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

因为我们是在代码中引用附加依赖项的,所以不用在vs里填了

在这里插入图片描述

运行项目

把博主那个视频文件拷贝到项目根目录下

在这里插入图片描述
发现有的代码不大对,我们改一下,
main.cpp

/*

ffmpeg lib 测试程序

*/

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <iostream>
#include <io.h>
#include <direct.h>

extern "C"
{
	#include <libavcodec/avcodec.h>
	#include <libavformat/avformat.h>
	#include <libavutil/imgutils.h>
	#include <libavutil/timestamp.h>		//av_ts2timestr
	#include <libavutil/samplefmt.h>
}

#define YUV420P_FILE	1	// 视频流保存成 yuv420p 图片
//#define H264_FILE	    1	// 视频流保存成 H264 文件

#ifdef _DEBUG
	#pragma comment(lib, "libavformatd.lib")
	#pragma comment(lib, "libavutild.lib")
#else
	//#pragma comment(lib, "libavformat.lib")
	#pragma comment(lib, "avformat.lib")
	//#pragma comment(lib, "libavutil.lib")
	#pragma comment(lib, "avutil.lib")
	#pragma comment(lib, "avcodec.lib")
	#pragma comment(lib, "avfilter.lib")
	#pragma comment(lib, "swscale.lib")
	#pragma comment(lib, "swresample.lib")
	#pragma comment(lib, "postproc.lib")
	#pragma comment(lib, "avdevice.lib")
#endif

using namespace std;

static int get_format_from_sample_fmt(const char** fmt, enum AVSampleFormat sample_fmt)
{
	int i;
	struct sample_fmt_entry 
	{
		enum AVSampleFormat sample_fmt; 
		const char* fmt_be, * fmt_le;
	} 
	sample_fmt_entries[] = 
	{
		{ AV_SAMPLE_FMT_U8,  "u8",    "u8"    },
		{ AV_SAMPLE_FMT_S16, "s16be", "s16le" },
		{ AV_SAMPLE_FMT_S32, "s32be", "s32le" },
		{ AV_SAMPLE_FMT_FLT, "f32be", "f32le" },
		{ AV_SAMPLE_FMT_DBL, "f64be", "f64le" },
	};
	*fmt = NULL;

	for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) 
	{
		struct sample_fmt_entry* entry = &sample_fmt_entries[i];
		if (sample_fmt == entry->sample_fmt) 
		{
			*fmt = AV_NE(entry->fmt_be, entry->fmt_le);
			return 0;
		}
	}

	fprintf(stderr,
		"sample format %s is not supported as output format\n",
		av_get_sample_fmt_name(sample_fmt));
	return -1;
}

// 参考:ffplay.c、demuxing_decoding.c
int main(int argc, char* argv[])
{
	int ret = 0;
	//printf("%s \n",avcodec_configuration());

	// 定义文件名
	const char input_filename[] = "video.mp4";
	const char out_filename[] = "video_out";
	char video_dst_filename[50]; // = "Video_Test_out.h264";
	char audio_dst_filename[50]; // = "Video_Test_out.aac";
	memset(video_dst_filename, '\0', 50);
	memset(audio_dst_filename, '\0', 50);

	// 1. 打开文件,分配AVFormatContext 结构体上下文
	AVFormatContext* fmt_ctx = NULL;	// 定义音视频格式上下文结构体
	if (avformat_open_input(&fmt_ctx, input_filename, NULL, NULL) < 0) 
	{
		printf("Could not open source file %s\n", input_filename);
		return 0;
	}
	// 2. 查找文件对应的流信息
	if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
		printf("Could not find stream information\n");
		return 0;
	}

	// 3. 打印流信息
	av_dump_format(fmt_ctx, 0, input_filename, 0);

	// 4. 视频解码器初始化
	// 4.1 获取视频对应的stream_index
	int video_stream_idx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
	printf("\n#===> Find video_stream_idx = %d\n", video_stream_idx);

	// 4.2 获取到stream 数据
	AVStream* video_st = fmt_ctx->streams[video_stream_idx];

	// 4.3 根据 codec_id 查找解码器
	AVCodec* video_dec = avcodec_find_decoder(video_st->codecpar->codec_id);
	// 4.4 初始化解码器上下文信息
	AVCodecContext* video_dec_ctx = avcodec_alloc_context3(video_dec);

	// 4.5 复制 codec 相关参数到解码器上下文中
	avcodec_parameters_to_context(video_dec_ctx, video_st->codecpar);
	printf("\n#===> Find decoder: %s,  coded_id:%d  long name: %s  pix_fmt=%d (%s)\n", video_dec->name, video_dec->id, video_dec->long_name, video_dec_ctx->pix_fmt, av_get_pix_fmt_name(video_dec_ctx->pix_fmt));

	// 4.6 初始化并打开解码器
	AVDictionary* video_opts = NULL;
	avcodec_open2(video_dec_ctx, video_dec, &video_opts);


	// 5. 音频解码器初始化
	// 5.1  获取音频对应的stream_index
	int audio_stream_idx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
	printf("\n#===> Find audio_stream_idx = %d\n", audio_stream_idx);

	// 5.2 获取到stream 数据
	AVStream* audio_st = fmt_ctx->streams[audio_stream_idx];

	// 5.3 根据 codec_id 查找解码器
	AVCodec* audio_dec = avcodec_find_decoder(audio_st->codecpar->codec_id);
	printf("\n#===> Find decoder: %s,  coded_id:%d  long name: %s\n", audio_dec->name, audio_dec->id, audio_dec->long_name);

	// 5.4 初始化解码器上下文信息
	AVCodecContext* audio_dec_ctx = avcodec_alloc_context3(audio_dec);

	// 5.5 复制 codec 相关参数到解码器上下文中
	avcodec_parameters_to_context(audio_dec_ctx, audio_st->codecpar);

	// 5.6 初化并打开音频解码器
	AVDictionary* audio_opts = NULL;
	avcodec_open2(audio_dec_ctx, audio_dec, &audio_opts);

	// 6. 配置视频解复用后要保存的位置
	FILE* video_dst_file = NULL, * audio_dst_file = NULL;

#ifdef H264_FILE
	sprintf_s(video_dst_filename, 50, "%s.%s", out_filename, video_dec->name);
	ret = fopen_s(&video_dst_file, video_dst_filename, "wb");
	printf("open file:%s   ret:%d\n", video_dst_filename, ret);
#endif

#ifdef YUV420P_FILE
	//文件夹名称
	char folderName[] = "video";

	// 文件夹不存在则创建文件夹
	if (_access(folderName, 0) == -1)
	{
		_mkdir(folderName);
	}

	snprintf(audio_dst_filename, 50, "video/%s.%s", out_filename, audio_dec->name);
#else
	sprintf_s(audio_dst_filename, 50, "%s.%s", out_filename, audio_dec->name);
#endif

	ret = fopen_s(&audio_dst_file, audio_dst_filename, "wb");
	printf("open file:%s   ret:%d\n", audio_dst_filename, ret);


	uint8_t* video_dst_data[4] = { NULL };
	int video_dst_linesize[4] = { 0 };

	// 7. 计算视频数据大小
	size_t video_dst_bufsize = av_image_alloc(video_dst_data, video_dst_linesize,
		video_dec_ctx->width, video_dec_ctx->height, video_dec_ctx->pix_fmt, 1);

	// 8. 分配并初始化 AVFrame、AVPacket
	AVFrame* frame = av_frame_alloc();
	AVPacket pkt;
	av_init_packet(&pkt);
	pkt.data = NULL;
	pkt.size = 0;
	int video_frame_count = 0, audio_frame_count = 0;

	printf("Start read frame\n");
	//  9. 循环读取 一帧数据
	while (av_read_frame(fmt_ctx, &pkt) >= 0) {
		// 10. 视频数据解码
		if (pkt.stream_index == video_stream_idx)
		{
			// 10.1 将 packet 数据 发送给解码器
			ret = avcodec_send_packet(video_dec_ctx, &pkt);

			// 10.2 获取解码后的帧数据
			while (ret >= 0) {
				ret = avcodec_receive_frame(video_dec_ctx, frame);

				if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
				{
					ret = 0;
					break;
				}
				// 10.3 保存帧数据到视频文件中
				//printf("#===> video_frame n:%d  coded_n:%d  ", video_frame_count++, frame->coded_picture_number);
				av_image_copy(video_dst_data, video_dst_linesize,
					(const uint8_t**)(frame->data), frame->linesize, video_dec_ctx->pix_fmt, video_dec_ctx->width, video_dec_ctx->height);
#ifdef YUV420P_FILE
				// 10.4 写入文件
				sprintf_s(video_dst_filename, 50, "video/%s.%s.%d.yuv", out_filename, av_get_pix_fmt_name(video_dec_ctx->pix_fmt), video_frame_count++);
				ret = fopen_s(&video_dst_file, video_dst_filename, "wb");
				//printf("open file:%s   ret:%d\n", video_dst_filename, ret);
#endif

				ret = (int)fwrite(video_dst_data[0], 1, video_dst_bufsize, video_dst_file);
				//printf("Write Size:%d\n", ret);
#ifdef YUV420P_FILE
				fclose(video_dst_file);
#endif
			}
		}
		// 11. 音频数据解码  
		else if (pkt.stream_index == audio_stream_idx)
		{
			// 11.1 将 packet 数据 发送给解码器
			ret = avcodec_send_packet(audio_dec_ctx, &pkt);

			// 11.2 获取解码后的帧数据
			while (ret >= 0) {
				ret = avcodec_receive_frame(audio_dec_ctx, frame);

				if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
				{
					ret = 0;
					break;
				}
				// 11.3 写入文件
				//printf("#===> audio_frame n:%d  nb_samples:%d  pts:%s ",
				//		audio_frame_count++, frame->nb_samples, av_ts2timestr(frame->pts, &audio_dec_ctx->time_base));
				ret = (int)fwrite(frame->extended_data[0], 1, frame->nb_samples * av_get_bytes_per_sample((AVSampleFormat)(frame->format)), audio_dst_file);
				//printf("Write Size:%d\n", ret);
			}
		}

		av_frame_unref(frame);
		// 清空AVPacket结构体数据
		av_packet_unref(&pkt);

		if (ret < 0)
			break;
	}

	// 12.发送一个空包,刷新解码器
	ret = avcodec_send_packet(video_dec_ctx, NULL);
	// 12.1 获取解码后的帧数据
	while (ret >= 0) {
		ret = avcodec_receive_frame(video_dec_ctx, frame);

		if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
		{
			printf("break, video ret=%d \n", ret);
			ret = 0;
			break;
		}
	}

	ret = avcodec_send_packet(audio_dec_ctx, NULL);

	// 12.2 获取解码后的帧数据
	while (ret >= 0) {
		ret = avcodec_receive_frame(audio_dec_ctx, frame);

		if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
		{
			printf("break, audio ret=%d \n", ret);
			ret = 0;
			break;
		}
	}


	// 13. 解复用完毕
	printf("Demuxing succeeded.\n");
	printf("Play the output video file with the command:\n"
		"ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n",
		av_get_pix_fmt_name(video_dec_ctx->pix_fmt), video_dec_ctx->width, video_dec_ctx->height, video_dst_filename);


	FILE* bat_dst_file = NULL;
	char bat_out[100] = "";
	memset(bat_out, '\0', 100);

#ifdef YUV420P_FILE
	memset(video_dst_filename, '\0', 50);
	sprintf_s(video_dst_filename, 50, "%s.%s.%d.yuv", out_filename, av_get_pix_fmt_name(video_dec_ctx->pix_fmt), 0);

#endif

	sprintf_s(bat_out, 99, "ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s",
		av_get_pix_fmt_name(video_dec_ctx->pix_fmt), video_dec_ctx->width, video_dec_ctx->height, video_dst_filename);

#ifdef YUV420P_FILE
	ret = fopen_s(&bat_dst_file, "video/play_video.bat", "wb");
#else
	ret = fopen_s(&bat_dst_file, "play_video.bat", "wb");
#endif

	ret = (int)fwrite(bat_out, 1, sizeof(bat_out), bat_dst_file);
	fclose(bat_dst_file);



	enum AVSampleFormat sfmt = audio_dec_ctx->sample_fmt;
	int n_channels = audio_dec_ctx->channels;
	const char* fmt;

	if (av_sample_fmt_is_planar(sfmt)) {
		const char* packed = av_get_sample_fmt_name(sfmt);
		printf("Warning: the sample format the decoder produced is planar "
			"(%s). This example will output the first channel only.\n",
			packed ? packed : "?");
		sfmt = av_get_packed_sample_fmt(sfmt);
		n_channels = 1;
	}
	if ((ret = get_format_from_sample_fmt(&fmt, sfmt)) < 0)
		goto end;
	printf("Play the output audio file with the command:\n"
		"ffplay -f %s -ac %d -ar %d %s\n",
		fmt, n_channels, audio_dec_ctx->sample_rate,
		audio_dst_filename);

	memset(bat_out, '\0', 100);
#ifdef YUV420P_FILE
	memset(audio_dst_filename, '\0', 50);
	sprintf_s(audio_dst_filename, 50, "%s.%s", out_filename, audio_dec->name);
#endif
	sprintf_s(bat_out, 99, "ffplay -f %s -ac %d -ar %d %s",
		fmt, n_channels, audio_dec_ctx->sample_rate,
		audio_dst_filename);
#ifdef YUV420P_FILE
	ret = fopen_s(&video_dst_file, "video/play_audio.bat", "wb");
#else
	ret = fopen_s(&video_dst_file, "play_audio.bat", "wb");
#endif
	ret = (int)fwrite(bat_out, 1, sizeof(bat_out), bat_dst_file);
	fclose(video_dst_file);

end:
	avcodec_free_context(&video_dec_ctx);
	avcodec_free_context(&audio_dec_ctx);
	avformat_close_input(&fmt_ctx);

#ifdef H264_FILE
	fclose(video_dst_file);
#endif

	fclose(audio_dst_file);
	av_frame_free(&frame);
	av_free(video_dst_data[0]);

	return 0;
}

然后vs上运行项目(注意重复运行项目时,不要打开里面生成的文件,以免占用)

在这里插入图片描述

查看文件

然后到项目根目录查看,发现多了个video目录,里面有一个.aac文件和很多YUV文件,除此之外,还有俩.bat脚本文件,

play_audio.bat:
ffplay -f f32le -ac 1 -ar 44100 video_out.aac

play_video.bat
ffplay -f rawvideo -pix_fmt yuv420p -video_size 1050x540 video_out.yuv420p.0.yuv

我们把ffmpeg可执行文件和动态库文件拷贝进来,然后执行那俩脚本文件,就能播放.aac文件和YUV文件了,不过貌似是播放视频的,我们生成的单帧文件,所以就只能播放出第一帧

在这里插入图片描述

双击play_audio.bat能播放.aac文件:

在这里插入图片描述

双击play_video.bat能播放查看YUV第一帧:

在这里插入图片描述

用YUView也能查看文件,不过要把图片正确的宽高和编码方式写对,宽1050,高540

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/8754.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringFramework:SpringBean的生命周期

SpringFramework&#xff1a;SpringBean的生命周期 文章目录SpringFramework&#xff1a;SpringBean的生命周期一、SpringBean的生命周期1. 实例化 Bean2. 填充属性&#xff08;DI&#xff09;3. 初始化4. 销毁二、BeanDefinition1. 基本概念2. 大致结构3. Spring 构建它的优势…

深度学习必备Python基础知识充电2

一、python中的类 1.1 python中是有内置的数据类型的 intstr 1.2 创建新的数据类型 自定义类来实现这样的功能 二、年轻人的第一个python类 2.1 来尝试一下 # 年轻人的第一个自定义python类class Man:def __init__(self, name):self.name nameprint(initialized Succes…

【优雅的参数验证@Validated】@Validated参数校验的使用及注解详解——你还在用if做条件验证?

Validated参数校验的使用及注解详解你还在用if做条件验证吗&#xff1f;一、优雅的参数验证Validated1.Valid和Validated的用法(区别)2.引入并使用Validated参数验证二、javax.validation.constraints下参数条件注解详解三、自定义条件注解你还在用if做条件验证吗&#xff1f; …

【云原生之K8s】 Pod控制器

文章目录一、Pod控制器及其功用二、控制器的类型1.Deployment2.StatefulSet2.1 StatefulSet的组成2.2 常规service和无头服务区别2.3 示例小结3.DaemonSet4.Job5.CronJob一、Pod控制器及其功用 Pod控制器&#xff0c;又称之为工作负载&#xff08;workload&#xff09;&#x…

【毕业设计】机器视觉火车票识别系统 - python 深度学习

文章目录0 前言1 课题意义1.1 课题难点&#xff1a;2 实现方法2.1 图像预处理2.2 字符分割2.3 字符识别2.3.1 部分实现代码3 实现效果4 最后0 前言 &#x1f525; Hi&#xff0c;大家好&#xff0c;这里是丹成学长的毕设系列文章&#xff01; &#x1f525; 对毕设有任何疑问…

疑似大厂泄露!阿里内部Redis教程笔记,细节点满/效率翻倍

Redis是一个key-value存储系统&#xff0c;是当下互联网公司广泛采用的NoSQL数据库之一&#xff0c;也是Java程序员应知应会的必备技术。 这套笔记教程采用Redis 6.2.1版本&#xff0c;内容由浅入深&#xff0c;循序渐进&#xff0c;从Redis的基本概念开启讲解&#xff0c;内容…

React核心技术浅析

1. JSX与虚拟DOM 我们从React官方文档开头最基本的一段Hello World代码入手: ReactDOM.render(<h1>Hello, world!</h1>,document.getElementById(root) );这段代码的意思是通过 ReactDOM.render() 方法将 h1 包裹的JSX元素渲染到id为“root”的HTML元素上. 除了在…

NVIDIA Grace Hopper架构深度解析

NVIDIA Grace Hopper架构深度解析 NVIDIA Grace Hopper Superchip 架构是第一个真正的异构加速平台&#xff0c;适用于高性能计算 (HPC) 和 AI 工作负载。 它利用 GPU 和 CPU 的优势加速应用程序&#xff0c;同时提供迄今为止最简单、最高效的分布式异构编程模型。 科学家和工程…

Python~Pandas 小白避坑之常用笔记

Python~Pandas 小白避坑之常用笔记 提示&#xff1a;该文章仅适合小白同学&#xff0c;如有错误的地方欢迎大佬在评论处赐教 文章目录Python~Pandas 小白避坑之常用笔记前言一、pandas安装二、数据读取1.读取xlsx文件2.读取csv文件三、重复值、缺失值、异常值处理、按行、按列剔…

pytest allure 生成报告过程

allure 下载地址&#xff1a;Releases allure-framework/allure2 GitHub 下载好后配置环境变量执行&#xff1a; allure --version 看见版本号就算配置成功了 pytest allure 生成报告过程 allure添加测试类名&#xff0c;方法名&#xff0c;步骤&#xff1a; allure.fea…

【附源码】计算机毕业设计JAVA教学成果管理平台录像演示

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven Vue 等等组成&#xff0c;B/…

教你一招轻松搞定mp3格式转换

第一种&#xff1a;ncm转mp3 经常使用网易云音乐的朋友应该会发现&#xff0c;网易云VIP音乐下载后&#xff0c;有些音乐是ncm格式的&#xff0c;无法导入PR或者一些编辑软件。 解决方法如下&#xff1a; 利用在线网站处理——Convertio 第一步&#xff1a;打开谷歌浏览器客户端…

9家美发连锁店老板一天剪辑1000个视频,用呆头鹅批量剪辑软件剪

1.1呆头鹅批量剪辑软件核心优势 01.我们的产品是经过市场考验的&#xff0c;像有结果的人学习&#xff0c;买有结果的产品。 02.3年的打磨&#xff0c;更新和删除了200多个模块 03.100多次持续优化更新 04.10000个公司和工作室和个人的使用和建议。 05.一个用户至少做出100…

磁盘有空间但无法创建文件

面试原题 我们去面试的时候,面试官通常会问一个问题, “小伙子,你在这些年的工作中,遇到过什么棘手的问题没有? 面试官问这个问题,无非想知道以下几件事情 你有没有过处理疑难问题的经验你解决问题的思路和能力如何你是怎么解决的你解决完这个问题有哪些收获 面试错误示范 …

Java生成验证码+动态分析技术+【实训10】HTML信息隐藏(信息安全技术作业)

Java生成验证码 第1关&#xff1a;使用Servlet生成验证码 任务要求参考答案评论 任务描述相关知识 为什么要有验证码&#xff0c;什么是验证码如何使用Servlet生成验证码编程要求测试说明任务描述 本关任务&#xff1a;使用servlet生成验证码。 相关知识 验证码在我们登陆…

硬链接及软连接引出的inode

inode定义 inode是linux系统中用作数据索引的标识符。简单来说&#xff0c;inode指示了一个文件的基本信息&#xff0c;如inode编号、修改时间、文件的位置等。 如同一本书的目录&#xff0c;会直接告诉你想看的章节是在第几页。不同的是&#xff0c;书是以页为单位的&#x…

软考 - 数据结构与算法

数据结构 线性结构 线性表 存储结构 顺序存储&#xff1a;用一组地址连续的存储单元 依次存储线性表中的数据元素&#xff0c;使得逻辑上相邻的元素物理上也相邻。 链式存储&#xff1a;存储各数据元素的结点的地址并不要求是连续的&#xff0c;数据元素逻辑上相邻&#xff…

提升Mac运行速度的三大方法

任何一部电子设备在使用多年之后都会出现性能下降的问题&#xff0c;苹果的Mac计算机自然也不例外。当你发现Mac运行缓慢&#xff0c;因为有太多文件或缓存垃圾将Mac的运行速度拖了下来。 要想提高生活和工作效率&#xff0c;必须对Mac进行优化&#xff0c;提升一下Mac 的使用性…

全业务链管理平台Odoo

什么是 Odoo ? Odoo 是一款非常容易使用又完全集成的商业应用&#xff0c;是一站式全业务链管理平台。 docker cli 安装 本项目涉及到 2 个容器&#xff0c;之前我们在下面&#x1f447;这些文章中 开源的看板管理工具Wekan类Trello的看板软件Planka群晖上安装MediaWiki的简…

linux系统java环境变量的下载与安装

由于目前好多工具的安全使用需要安装java环境&#xff0c;所有今天就分享一下java环境变量的安装与配置下载地址&#xff1a; https://download.oracle.com/otn/java/jdk/8u351-b10/10e8cce67c7843478f41411b7003171c/jdk-8u351-linux-i586.tar.gz?AuthParam1668564371_517fa4…