ffmpeg视频编解码 demo初探(二)(包含下载指定windows版本ffmpeg)将YUV图片序列作为流读入,编码封装成x264 MP4视频

news2024/11/19 15:18:10

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

文章目录

    • 第二个项目:将YUV图片序列作为流读入,编码封装成x264 MP4视频
      • 将YUV图片序列编码成.h264文件
      • 将YUV图片序列编码成mp4文件

第二个项目:将YUV图片序列作为流读入,编码封装成x264 MP4视频

将YUV图片序列编码成.h264文件

直接把博主的代码拷到我们继承的上一个项目中,感觉应该有一些地方需要修改,果不其然

#pragma warning(disable : 4996)
#include <iostream>
#include <string>       // 字符串操作
#include <direct.h>     // 文件夹

extern "C" {            // ffmpeg 相关头文件
#include <libavutil/avassert.h>
#include <libavutil/channel_layout.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include "libavutil/imgutils.h"
}

using namespace std;


#pragma comment(lib, "avformat.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")

// C++ 中 ffmpeg有些地方报错,修改这样后 OK
//
static char* av_ts_make_string1(char* buf, int64_t ts)
{
	if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
	else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%" PRId64, ts);
	return buf;
}

static char* av_ts_make_time_string1(char* buf, int64_t ts, AVRational* tb)
{
	if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
	else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
	return buf;
}
//



/// <summary>
/// 输入输出文件信息
/// </summary>
const char* Out_File_Name = "video.h264";

// 源文件名:video/video_out.yuv420p.0.yuv 
const char* Folder = "video";
const char* YUV_File_Name = "video_out.yuv420p";
const char* YUV = "yuv";			// 图片文件后缀
#define YUV_Width	1050			// 宽
#define YUV_Height	540				// 高
#define Stream_Frame_Rate	25		// 帧率:每秒25帧

// 参考:doc\examples\encode_video.c、doc\examples\muxing.c

int main(int* argc, char* argv[])
{

	// 1. 初始化视频文件格式上下文
	AVFormatContext* p_FormatCtx = NULL;

	avformat_alloc_output_context2(&p_FormatCtx, NULL, "h264", Out_File_Name);
	if (!p_FormatCtx) {
		cout << "无法从输出文件后缀判断视频格式,默认使用h264" << endl;
		avformat_alloc_output_context2(&p_FormatCtx, NULL, "h264", Out_File_Name);
	}

	// 2. 获得输出格式 Format信息
	AVOutputFormat* p_OutputFmt = NULL;
	p_OutputFmt = p_FormatCtx->oformat;


	bool have_video = false, have_audio = false, encode_video = false, encode_audio = false;
	AVCodec* p_Video_Enc = NULL, * p_Vudio_Enc = NULL;
	AVCodecContext* p_Video_Enc_Ctx = NULL, * p_Audio_Enc_Ctx = NULL;
	AVStream* p_Video_st = NULL, * p_Audio_st = NULL;
	AVFrame* p_Video_Frame = NULL;

	// 3. 添加 视频输出流,查找并打开视频解码器
	if (p_OutputFmt->video_codec != AV_CODEC_ID_NONE) {
		// 3.1 找到视频解码器
		p_Video_Enc = avcodec_find_encoder(p_OutputFmt->video_codec);

		// 3.2 创建视频流
		p_Video_st = avformat_new_stream(p_FormatCtx, p_Video_Enc);
		p_Video_st->id = p_FormatCtx->nb_streams - 1;

		// 3.3 创建编码器上下文
		p_Video_Enc_Ctx = avcodec_alloc_context3(p_Video_Enc);

		// 3.4 配置视频格式
		p_Video_Enc_Ctx->codec_type = AVMediaType::AVMEDIA_TYPE_VIDEO;
		p_Video_Enc_Ctx->codec_id = p_OutputFmt->video_codec;	// Code id
		p_Video_Enc_Ctx->pix_fmt = AVPixelFormat::AV_PIX_FMT_YUV420P;			// 图片格式

		p_Video_Enc_Ctx->bit_rate = 2000000;	// 码率越高,画面质量越好,相应视频越大	// 采样器码率
		p_Video_Enc_Ctx->width = YUV_Width;						// 宽
		p_Video_Enc_Ctx->height = YUV_Height;					// 高

		p_Video_st->time_base.num = 1;					// 音位 1s
		p_Video_st->time_base.den = 25;					// 帧率,每秒25帧


		p_Video_Enc_Ctx->framerate.num = 25;
		p_Video_Enc_Ctx->framerate.den = 1;
		p_Video_Enc_Ctx->time_base = p_Video_st->time_base;

		p_Video_Enc_Ctx->gop_size = 12;							// 连续画面组大小

		if (p_Video_Enc_Ctx->codec_id == AV_CODEC_ID_H264)
		{
			p_Video_Enc_Ctx->qmin = 10;				// 最小的量化因子
			p_Video_Enc_Ctx->qmax = 51;				// 最大的量化因子
			p_Video_Enc_Ctx->qcompress = 0.6;
		}


		if (p_Video_Enc_Ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO)
			p_Video_Enc_Ctx->max_b_frames = 2;
		if (p_Video_Enc_Ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO)
			p_Video_Enc_Ctx->mb_decision = 2;

		if (p_OutputFmt->flags & AVFMT_GLOBALHEADER)
			p_Video_Enc_Ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

		// 3.5 打开编码器
		avcodec_open2(p_Video_Enc_Ctx, p_Video_Enc, NULL);
		avcodec_parameters_from_context(p_Video_st->codecpar, p_Video_Enc_Ctx);

		// 3.6 分配视频帧内存
		p_Video_Frame = av_frame_alloc();
		p_Video_Frame->format = p_Video_Enc_Ctx->pix_fmt;
		p_Video_Frame->width = p_Video_Enc_Ctx->width;
		p_Video_Frame->height = p_Video_Enc_Ctx->height;
		av_frame_get_buffer(p_Video_Frame, 0);

		have_video = true;
		encode_video = true;
	}

	// 预留
	// 4. 添加 音频输出流,查找并打开音频解码器
	if (p_OutputFmt->audio_codec != AV_CODEC_ID_NONE) {
		have_audio = true;
		encode_video = true;
	}

	// 5. 打印输出信息
	av_dump_format(p_FormatCtx, 0, Out_File_Name, 1);

	// 6. 打开输出文件
	if (!(p_OutputFmt->flags & AVFMT_NOFILE))
		avio_open(&p_FormatCtx->pb, Out_File_Name, AVIO_FLAG_WRITE);

	// 7. 写头信息
	avformat_write_header(p_FormatCtx, NULL);

	char pic_file_name[50] = "";
	int pic_index = 0;
	const int pic_size = YUV_Height * YUV_Width * 3 / 2;
	FILE* pic_file = NULL;
	uint8_t* pic_buff = (uint8_t*)av_malloc(pic_size);

	int file_end = 0;

	int ret;
	char errbuf[AV_ERROR_MAX_STRING_SIZE] = { 0 };

	int y, x;

	// 8. 开始循环写入数据
	while (file_end == 0) {
		// 打开文件 获取数据
		{
			memset(pic_file_name, '\0', 50);
			sprintf_s(pic_file_name, 50, "%s/%s.%d.%s", Folder, YUV_File_Name, pic_index, YUV);
			cout << "打开文件:" << pic_file_name << endl;

			// 注意,此处一定要,使用 "rb+" 否则一次读取的数据大小远小于预期的大小
			pic_file = fopen(pic_file_name, "rb+");			// fopen(&pic_file, pic_file_name, "rb+")
			if (pic_file == NULL) {
				cout << "打开失败:" << pic_file_name << ", 开始刷新编码缓冲区\n";
				file_end = 1;
			}

			if (file_end == 0) {
				//ret = fread_s(pic_buff, pic_size, 1, pic_size, pic_file);
				ret = fread(pic_buff, 1, pic_size, pic_file);
				cout << "读取文件大小为:" << ret << "  应读大小:" << pic_size << endl;
				if (ret <= 0) {
					cout << "读取内容失败,ret=" << ret << endl;
					break;
				}
				fclose(pic_file);
				pic_file = NULL;

				/* make sure the frame data is writable */
				ret = av_frame_make_writable(p_Video_Frame);
				if (ret < 0)
				{
					cout << "p_Video_Frame data 不可写,强制退出\n";
					exit(1);
				}

				for (x = 0, y = 0; x < YUV_Height; x++)
				{
					memcpy(p_Video_Frame->data[0] + x * p_Video_Frame->linesize[0], pic_buff + y, YUV_Width);
					y += YUV_Width;
				}
				for (x = 0, y = 0; x < YUV_Height / 2; x++)
				{
					memcpy(p_Video_Frame->data[1] + x * p_Video_Frame->linesize[1], pic_buff + y + YUV_Width * YUV_Height, YUV_Width / 2);
					y += YUV_Width / 2;
				}
				for (x = 0, y = 0; x < YUV_Height / 2; x++)
				{
					memcpy(p_Video_Frame->data[2] + x * p_Video_Frame->linesize[2], pic_buff + y + YUV_Width * YUV_Height * 5 / 4, YUV_Width / 2);
					y += YUV_Width / 2;
				}

				//赋值
					///* prepare a dummy image */
					///* Y */
					//for (y = 0; y < p_Video_Enc_Ctx->height; y++) {
					//	for (x = 0; x < p_Video_Frame->linesize[0]; x++) {
					//		if (x < YUV_Width)
					//			p_Video_Frame->data[0][y * p_Video_Frame->linesize[0] + x] = pic_buff[y * YUV_Height + x];								//x + y + pic_index * 3
					//		else
					//			p_Video_Frame->data[0][y * p_Video_Frame->linesize[0] + x] = 128;
					//	}
					//}
					//
					///* Cb and Cr */
					//for (y = 0; y < p_Video_Enc_Ctx->height / 2; y++) {
					//	for (x = 0; x < p_Video_Frame->linesize[1]; x++) {
					//		if (x < YUV_Width / 2) {
					//			p_Video_Frame->data[1][y * p_Video_Frame->linesize[1] + x] = pic_buff[y * YUV_Height/2 + x + YUV_Height * YUV_Width];	//128 + y + pic_index * 2;
					//			p_Video_Frame->data[2][y * p_Video_Frame->linesize[2] + x] = pic_buff[y * YUV_Height/2 + x + YUV_Height * YUV_Width * 5 / 4];	// 64 + x + pic_index * 5;
					//		}
					//		else {
					//			p_Video_Frame->data[1][y * p_Video_Frame->linesize[1] + x] = 128;
					//			p_Video_Frame->data[2][y * p_Video_Frame->linesize[2] + x] = 128;
					//		}

					//	}
					//}
					//
					//cout << " linesize: " << p_Video_Frame->linesize[0] << "  " << p_Video_Frame->linesize[1] << "  " << p_Video_Frame->linesize[2] << "  " << p_Video_Frame->linesize[3] << endl;
			}
		}

		// 开始编码
		{
			// PTS: 设置播放时间
			p_Video_Frame->pts = pic_index * (p_Video_st->time_base.den) / (p_Video_st->time_base.num * 25);
			// Encode 开始编码
			if (file_end == 0)
				ret = avcodec_send_frame(p_Video_Enc_Ctx, p_Video_Frame);
			else
				ret = avcodec_send_frame(p_Video_Enc_Ctx, NULL);
			if (ret < 0) {
				cout << "编码失败:" << av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE) << endl;
				exit(1);
			}
			// 接收编码后的数据
			while (ret >= 0) {
				AVPacket pkt = { 0 };
				ret = avcodec_receive_packet(p_Video_Enc_Ctx, &pkt);
				if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
					break;
				else if (ret < 0) {
					cout << "编码失败:" << av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE) << endl;
					break;
				}

				av_packet_rescale_ts(&pkt, p_Video_Enc_Ctx->time_base, p_Video_st->time_base);
				pkt.stream_index = p_Video_st->index;

				AVRational* time_base = &p_FormatCtx->streams[pkt.stream_index]->time_base;
				cout << "pts:" << av_ts_make_string1(errbuf, pkt.pts) << " pts_time:" << av_ts_make_time_string1(errbuf, pkt.pts, time_base);
				cout << " dts:" << av_ts_make_string1(errbuf, pkt.dts) << " duration:" << av_ts_make_string1(errbuf, pkt.duration);
				cout << " duration_time:" << av_ts_make_time_string1(errbuf, pkt.duration, time_base) << " stream_index:" << pkt.stream_index << endl;

				// 将编码后的数据写入文件中
				ret = av_interleaved_write_frame(p_FormatCtx, &pkt);
				av_packet_unref(&pkt);

				if (ret < 0) {
					cout << "Error while writing output packet: " << av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE) << endl;
					break;
				}
			}
		}

		pic_index++;
	}

	// 9. 写入尾信息
	av_write_trailer(p_FormatCtx);

	// 10.关闭文件
	if (!(p_FormatCtx->flags & AVFMT_NOFILE))
		avio_closep(&p_FormatCtx->pb);



	// 11. 释放资源
	if (have_video) {
		if (pic_buff)
			av_free(pic_buff);
		if (p_Video_Frame)
			av_frame_free(&p_Video_Frame);
		if (p_Video_Enc_Ctx)
			avcodec_free_context(&p_Video_Enc_Ctx);	// 释放视频解码器
	}

	if (p_FormatCtx)
		avformat_free_context(p_FormatCtx);

	system("ffplay video.h264");

	return 0;
}

在这里插入图片描述
跑完后,项目根目录多了个video.h264文件

用VLC播放器能打开浏览

在这里插入图片描述

在这里插入图片描述

将YUV图片序列编码成mp4文件

参考文章:ffmpeg:将YUV原始数据编码封装为mp4格式

还是老样子,把博主代码搞下来,vs配置一下,跑

#pragma warning(disable : 4996)

// 2-muxing编码视频.cpp

#include <iostream>
#include <string>       // 字符串操作
#include <direct.h>     // 文件夹
#include <stdbool.h>

extern "C" {            // ffmpeg 相关头文件
#include "libavcodec/avcodec.h"
#include <libavutil/opt.h>
#include "libavutil/imgutils.h"
}

using namespace std;

#pragma comment(lib, "avformat.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")

/// 输入输出文件信息
//const char* Out_File_Name = "video.h264";
const char* Out_File_Name = "video_out.mp4";

// 源文件名:video/video_out.yuv420p.0.yuv 
const char* Folder = "video";
const char* YUV_File_Name = "video_out.yuv420p";
const char* YUV = "yuv";			// 图片文件后缀
#define YUV_Width	1050			// 宽
#define YUV_Height	540				// 高
#define Stream_Frame_Rate	25		// 帧率:每秒25帧

// 参考:doc\examples\encode_video.c

int main(int* argc, char* argv[])
{
	// 1. 通过名字查找Codec 编码器
	AVCodec* p_Video_Enc = NULL;
	//p_Video_Enc = avcodec_find_encoder(AV_CODEC_ID_H264);	// 可改进为通过后缀判断
	p_Video_Enc = avcodec_find_encoder(AV_CODEC_ID_MPEG4);	// 可改进为通过后缀判断
	if (!p_Video_Enc) {
		cout << "h264 Codec not foud\n";
		return -1;
	}
	// 2. 获取 Codec 上下文
	AVCodecContext* p_Video_Enc_Ctx = NULL;
	p_Video_Enc_Ctx = avcodec_alloc_context3(p_Video_Enc);

	// 3. 配置编码信息
	p_Video_Enc_Ctx->bit_rate = 2000000;	// 配置码率
	p_Video_Enc_Ctx->width = YUV_Width;		// 宽
	p_Video_Enc_Ctx->height = YUV_Height;	// 高
	// 配置帧率,25
	p_Video_Enc_Ctx->time_base.num = 1;
	p_Video_Enc_Ctx->time_base.den = Stream_Frame_Rate;
	p_Video_Enc_Ctx->framerate.num = Stream_Frame_Rate;
	p_Video_Enc_Ctx->framerate.den = 1;

	p_Video_Enc_Ctx->gop_size = 12;				// 连续画面组大小
	//p_Videc_Enc_Ctx->max_b_frames = 30;		// 最大连续b 帧数
	p_Video_Enc_Ctx->pix_fmt = AV_PIX_FMT_YUV420P;
	p_Video_Enc_Ctx->codec_type = AVMEDIA_TYPE_VIDEO;
	p_Video_Enc_Ctx->codec_id = p_Video_Enc->id;

	if (p_Video_Enc_Ctx->codec_id == AV_CODEC_ID_H264)
	{
		p_Video_Enc_Ctx->qmin = 10;				// 最小的量化因子
		p_Video_Enc_Ctx->qmax = 51;				// 最大的量化因子
		p_Video_Enc_Ctx->qcompress = 0.6;
	}

	if (p_Video_Enc_Ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO)
		p_Video_Enc_Ctx->max_b_frames = 2;
	if (p_Video_Enc_Ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO)
		p_Video_Enc_Ctx->mb_decision = 2;

	if (p_Video_Enc->id == AV_CODEC_ID_H264)
		av_opt_set(p_Video_Enc_Ctx->priv_data, "preset", "slow", 0);

	// 4. 打开编码器
	if (avcodec_open2(p_Video_Enc_Ctx, p_Video_Enc, NULL) < 0) {
		cout << "打开编码器失败\n";
		avcodec_free_context(&p_Video_Enc_Ctx);
		return -1;
	}

	// 5. 分配 Pkt,frame包内存
	AVPacket* p_Video_Pkt = NULL;
	p_Video_Pkt = av_packet_alloc();
	AVFrame* p_Video_Frame = NULL;
	p_Video_Frame = av_frame_alloc();
	p_Video_Frame->format = p_Video_Enc_Ctx->pix_fmt;
	p_Video_Frame->width = p_Video_Enc_Ctx->width;
	p_Video_Frame->height = p_Video_Enc_Ctx->height;
	av_frame_get_buffer(p_Video_Frame, 0);

	// 6. 打开输出文件
	FILE* p_Out_File = NULL;
	p_Out_File = fopen(Out_File_Name, "wb");
	if (!p_Out_File) {
		cout << "打开 《" << Out_File_Name << "》 文件失败\n";
		return -1;
	}

	// 7. 开始编码并写入文件
	char pic_file_name[50] = "";
	int pic_index = 0, x, y, ret;
	uint8_t* pic_buff = (uint8_t*)av_malloc(YUV_Height * YUV_Width * 3 / 2);
	FILE* pic_file = NULL;
	bool file_end = false;

	while (file_end == false) {
		// 打开文件 获取数据
		memset(pic_file_name, '\0', 50);
		sprintf_s(pic_file_name, 50, "%s/%s.%d.%s", Folder, YUV_File_Name, pic_index, YUV);
		cout << "打开文件:" << pic_file_name << " ";

		// 注意,此处一定要,使用 "rb+" 否则一次读取的数据大小远小于预期的大小
		pic_file = fopen(pic_file_name, "rb+");			// fopen(&pic_file, pic_file_name, "rb+")
		if (pic_file == NULL) {
			cout << "打开失败:" << pic_file_name << ", 开始刷新编码缓冲区\n";
			file_end = true;
		}

		if (file_end == false) {
			ret = fread(pic_buff, 1, YUV_Height * YUV_Width * 3 / 2, pic_file);
			cout << "读取文件大小为:" << ret;
			if (ret <= 0) {
				cout << "读取内容失败,ret=" << ret << endl;
				break;
			}
			fclose(pic_file);
			pic_file = NULL;

			/* make sure the frame data is writable */
			ret = av_frame_make_writable(p_Video_Frame);
			if (ret < 0)
			{
				cout << "p_Video_Frame data 不可写,强制退出\n";
				exit(1);
			}

			for (x = 0, y = 0; x < YUV_Height; x++) {
				memcpy(p_Video_Frame->data[0] + x * p_Video_Frame->linesize[0], pic_buff + y, YUV_Width);
				y += YUV_Width;
			}
			for (x = 0, y = 0; x < YUV_Height / 2; x++) {
				memcpy(p_Video_Frame->data[1] + x * p_Video_Frame->linesize[1], pic_buff + y + YUV_Width * YUV_Height, YUV_Width / 2);
				y += YUV_Width / 2;
			}
			for (x = 0, y = 0; x < YUV_Height / 2; x++) {
				memcpy(p_Video_Frame->data[2] + x * p_Video_Frame->linesize[2], pic_buff + y + YUV_Width * YUV_Height * 5 / 4, YUV_Width / 2);
				y += YUV_Width / 2;
			}

			// PTS: 设置播放时间
			p_Video_Frame->pts = pic_index * (p_Video_Enc_Ctx->time_base.den) / (p_Video_Enc_Ctx->time_base.num * 25);
			pic_index++;

			cout << "  Frame: pts = " << p_Video_Frame->pts << " linesize = " << p_Video_Frame->linesize[0] << " " << p_Video_Frame->linesize[1] << " " << p_Video_Frame->linesize[2] << endl;
		}

		// Encode 开始编码
		if (file_end == false)
			ret = avcodec_send_frame(p_Video_Enc_Ctx, p_Video_Frame);
		else {
			cout << "发送一个空 Frame 包,用于刷新编码缓冲区\n";
			ret = avcodec_send_frame(p_Video_Enc_Ctx, NULL);
		}

		// 接收编码后的数据
		while (ret >= 0) {
			ret = avcodec_receive_packet(p_Video_Enc_Ctx, p_Video_Pkt);
			if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
				break;
			else if (ret < 0) {
				cout << "编码失败:" << endl;
				break;
			}

			cout << "编码完毕 Video Packet " << p_Video_Pkt->pts << " (size = " << p_Video_Pkt->size << ")\n";

			fwrite(p_Video_Pkt->data, 1, p_Video_Pkt->size, p_Out_File);

			av_packet_unref(p_Video_Pkt);
		}
	}

	/* add sequence end code to have a real MPEG file */
	if (p_Video_Enc->id == AV_CODEC_ID_MPEG1VIDEO || p_Video_Enc->id == AV_CODEC_ID_MPEG2VIDEO) {
		uint8_t endcode[] = { 0, 0, 1, 0xb7 };
		fwrite(endcode, 1, sizeof(endcode), p_Out_File);
	}

	fclose(p_Out_File);

	if (p_Video_Frame)
		av_frame_free(&p_Video_Frame);
	if (p_Video_Pkt)
		av_packet_free(&p_Video_Pkt);
	if (p_Video_Enc_Ctx) {
		avcodec_close(p_Video_Enc_Ctx);
		avcodec_free_context(&p_Video_Enc_Ctx);
	}

	//system("ffplay video.h264");
	return 0;
}

在这里插入图片描述
生成了一个.mp4文件

在这里插入图片描述
用qq影音和windows media player能正常打开

在这里插入图片描述

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

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

相关文章

艾美捷测序级 II,纯化胰蛋白酶化验程序文献参考

胰蛋白酶是一种基于带正电荷的赖氨酸和精氨酸侧链的底物特异性胰丝氨酸蛋白酶&#xff08;Brown and Wold 1973&#xff09;。这种酶由胰腺排出&#xff0c;参与食物蛋白质的消化和其他生物过程。胰蛋白酶是一种中等大小的球状蛋白&#xff0c;作为一种无活性的胰蛋白酶原产生&…

甘露糖-顺铂mannose-cisplatin|甘露糖-聚乙二醇-顺铂cisplatin-PEG-mannose

甘露糖-顺铂mannose-cisplatin|甘露糖-聚乙二醇-顺铂cisplatin-PEG-mannose 顺铂&#xff0c;又名顺式-二氯二氨合铂&#xff0c;是一种含铂的药物&#xff0c;呈橙黄色或黄色结晶性粉末&#xff0c;微溶于水、易溶于二甲基甲酰胺&#xff0c;在水溶液中可逐渐转化成反式和水解…

基于Feign接口的全链路拦截器

1、前言 单体应用时&#xff0c;我们经常会把一些共享数据&#xff0c;比如登录信息等放在session里面&#xff0c;当然也可以放在ThreadLocal里面。随着业务复杂度的提高&#xff0c;分布式应用越来越主流。单机的存储的思想已经不适用了&#xff0c;共享session应运而生&…

如何度量预测用户付费的误差

在广告&#xff0c;电商&#xff0c;游戏等行业中&#xff0c;预测用户付费是核心的业务场景&#xff0c;能直接帮助提升收入&#xff0c;利润等核心业务指标&#xff0c;堪称预测中的明星。在预测用户付费的系列文章中&#xff0c;结合作者理论和工程实践经验&#xff0c;深入…

C++ make_heap等堆函数的使用

一、介绍 C的STL提供了make_heap、push_heap、pop_heap、sort_heap等算法&#xff0c;它们用来将一个随机存储的数组或者容器等转换为一个heap。这里所说的转换为heap意思是将原来的存储顺序改变&#xff0c;将转换成的堆层序遍历后所得到的元素顺序作为数组或者容器新的元素顺…

用HarmonyOS ArkUI调用三方库PhotoView实现图片的联播、缩放

本文演示如果用HarmonyOS的ArkUI来调用已经上架到三方库中心的社区库。体验HarmonyOS 3最新的API 9&#xff0c;欢迎大家一起参与构建这个万物互联的时代&#xff01; 活动主页 HarmonyOS线上Codelabs挑战赛已经开启&#xff0c;该系列挑战赛将围绕HarmonyOS的基础组件和容器…

ABAQUS计算不收敛问题,排查方法和解决方案都在这儿了

在进行有限元仿真计算时&#xff0c;常常会遇到计算不收敛的问题&#xff0c;而且导致求解不收敛的原因也是多种多样的&#xff0c;处理起来也是相当的麻烦。特别是在利用隐式算法的求解非线性问题时&#xff0c;对静态平衡方程进行迭代求解时极易出现计算的不收敛问题&#xf…

JVM垃圾回收——垃圾收集器(一)

目录 一、垃圾收集器 二、Serial/Serial Old 三、ParNew 收集器 四、Parallel Scavenge收集器 五、Parallel Old收集器 一、垃圾收集器 现阶段可以作为商用的垃圾收集器大概以上几种&#xff0c;ZGC还正在实验阶段&#xff0c;如果两个收集器之间有连线那么表示他们可搭配…

【Linux初阶】Linux环境下的 git 使用 | git的add/commit/push/log/pull/mv/rm/status

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【Linux初阶】 ✒️✒️本篇内容&#xff1a;详细阐述git是什么&#xff0c;git的发展脉络&#xff0c;还有Linux环境下git工具的具体使用方法 &#x1f6a…

关于如何导入OTWB 7型项目

一、简介&#xff1a; 公司最新的 OTWB 7.0 产品&#xff0c;均使用最新版开发开发框架 ThornForest&#xff0c;简称 TF。相较于 Thorn 框架&#xff0c;新版的 TF 框架&#xff0c;在页面编译的方式有比较大的变化。其中最主要的变化是&#xff0c;TF 的编译需要根据不同部署…

深入理解JavaScript——执行上下文与调用栈

前言 在说一个概念前&#xff0c;我们需要确定它的前提&#xff0c;此文以 ECMAScript5 为基础撰写 一句话解释 执行上下文就是一段代码执行时所带的所有信息 执行上下文是什么 《重学前端》的作者 winter 曾经对什么是执行上下文做过这样的解释&#xff1a; JavaScript 标…

基于基于全局差错能量函数的双目图像立体匹配算法matlab仿真,并提取图像的深度信息

目录 1.算法概述 2.仿真效果预览 3.核心MATLAB代码预览 4.完整MATLAB程序 1.算法概述 全局的能量函数公式如下: E(f)Edata(f)Esmooth(f) 其中,Edata 表示能量函数的数据项,意为该像素只考虑自身的视差值的倾向,不考虑 邻域内其他像素的影响;N 表示匹配聚合时的支持窗口;p 表…

应用层-HTTP协议

HTTP概述 HTTP(HyperTextTransferProtocol)是Web应用的应用层协议&#xff0c;定义浏览器如何向Web服务器发送请求以及Web服务器如何向浏览器进行响应。目前主要使用的HTTP/1.0 和HTTP/1.1&#xff0c;尤其以HTTP/1.1 为主流。 HTTP连接 浏览器在向服务器发送请求之前&#…

全日制和非全日制之争,看完六年前的这个文件心里就有数了

在每年的报考咨询中&#xff0c;都能接触到不少关于非全日制硕士的质疑&#xff0c;最大的争议点无非在于社会含金量的问题。其实很多年以前是没有非全日制这一说法的&#xff0c;早些年很多学员也是在职周末读的双证MBA/MPA/MEM这些专业&#xff0c;但证书一律是全日制标识&am…

Oracle Primavera Unifier进度管理器(Schedule Manager)

目录 功能介绍 功能包括 功能介绍 在进度管理器中&#xff0c;Primavera Unifier 用户可以在项目/外壳和项目群级别创建和管理进度表。他们可以创建根据项目或外壳的需求自定义的项目/外壳计划表。当他们为项目/外壳创建第一个时间表表时&#xff0c;Primavera Unifier 会自…

【软考】系统集成项目管理工程师(九)项目成本管理

这里写目录标题 一、项目成本管理概述二、项目成本管理子过程1. 规划成本2. 成本估算3. 制定预算4. 控制成本一、项目成本管理概述 成本 即项目的全过程中所耗用的各种成本,它们的总和为项目成本。成本管理 是在预算范围内确保项目团队完成一个项目所需要开展的管理过程,项目…

前端网页项目-学成在线案例

典型的企业级网站目的是为了整体感知企业级网站布局流程&#xff0c;复习以前知识 准备素材和工具&#xff1a; 学成在线PSD源文件开发工具PS&#xff08;切图&#xff09;/cutterman插件vscode&#xff08;代码&#xff09;chrome&#xff08;测试&#xff09; 案例准备工作&…

[附源码]java毕业设计健身健康规划系统

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

发布 .NET 7 MAUI / MAUI Blazor 应用到 Windows 应用商店

.NET MAUI 目前仅允许发布 MSIX 包。 原文地址 https://www.cnblogs.com/densen2014/p/16885318.html 创建签名证书发布到本地传送门 https://www.cnblogs.com/densen2014/p/16567384.html 使用 Visual Studio 2022 发布到 Windows 应用商店 由于是发布到 Windows 应用商店,本…

代码中可能会使用

代码中可能会使用 日志 结合兼具举报系统日志 https://www.cnblogs.com/lingduqianli/p/7589173.html 拦截器 https://blog.csdn.net/neymar_jr/article/details/79115839 拦截器应用场景 拦截器本质上是面向切面编程&#xff08;AOP&#xff09;&#xff0c;符合横切关注点…